Browse Source

Merge branch 'master' of github.com:falkTX/Carla

tags/v1.9.11
falkTX 6 years ago
parent
commit
9fa3fb1135
100 changed files with 6257 additions and 2273 deletions
  1. +12
    -5
      .gitignore
  2. +6
    -3
      Makefile
  3. +19
    -0
      data/macos/mac-build-test.sh
  4. +31
    -0
      data/valgrind.sh
  5. +56
    -0
      data/valgrind.supp
  6. +2
    -2
      source/backend/CarlaBackend.doxygen
  7. +4
    -4
      source/backend/CarlaBackend.h
  8. +16
    -6
      source/backend/CarlaEngine.hpp
  9. +10
    -1
      source/backend/CarlaHost.h
  10. +2
    -2
      source/backend/CarlaHostCommon.cpp
  11. +2
    -2
      source/backend/CarlaPlugin.hpp
  12. +90
    -15
      source/backend/CarlaStandalone.cpp
  13. +0
    -837
      source/backend/CarlaUtils.cpp
  14. +9
    -1
      source/backend/CarlaUtils.h
  15. +4
    -32
      source/backend/Makefile
  16. +71
    -48
      source/backend/engine/CarlaEngine.cpp
  17. +5
    -11
      source/backend/engine/CarlaEngineBridge.cpp
  18. +35
    -0
      source/backend/engine/CarlaEngineData.cpp
  19. +31
    -87
      source/backend/engine/CarlaEngineInternal.cpp
  20. +8
    -12
      source/backend/engine/CarlaEngineInternal.hpp
  21. +122
    -57
      source/backend/engine/CarlaEngineJack.cpp
  22. +4
    -3
      source/backend/engine/CarlaEngineNative.cpp
  23. +33
    -18
      source/backend/engine/CarlaEngineRtAudio.cpp
  24. +8
    -12
      source/backend/engine/CarlaEngineThread.cpp
  25. +6
    -2
      source/backend/engine/Makefile
  26. +88
    -79
      source/backend/plugin/CarlaPlugin.cpp
  27. +49
    -23
      source/backend/plugin/CarlaPluginBridge.cpp
  28. +12
    -12
      source/backend/plugin/CarlaPluginDSSI.cpp
  29. +8
    -7
      source/backend/plugin/CarlaPluginInternal.cpp
  30. +4
    -3
      source/backend/plugin/CarlaPluginInternal.hpp
  31. +8
    -7
      source/backend/plugin/CarlaPluginJack.cpp
  32. +8
    -5
      source/backend/plugin/CarlaPluginLADSPA.cpp
  33. +20
    -17
      source/backend/plugin/CarlaPluginLV2.cpp
  34. +35
    -2
      source/backend/plugin/CarlaPluginNative.cpp
  35. +5
    -5
      source/backend/plugin/CarlaPluginSFZero.cpp
  36. +20
    -13
      source/backend/plugin/CarlaPluginVST2.cpp
  37. +576
    -0
      source/backend/utils/CachedPlugins.cpp
  38. +158
    -0
      source/backend/utils/Information.cpp
  39. +72
    -0
      source/backend/utils/Makefile
  40. +169
    -0
      source/backend/utils/PipeClient.cpp
  41. +41
    -0
      source/backend/utils/System.cpp
  42. +108
    -0
      source/backend/utils/Windows.cpp
  43. +4
    -3
      source/bridges-plugin/CarlaBridgeSingleLV2.cpp
  44. +3
    -5
      source/bridges-plugin/Makefile
  45. +0
    -4
      source/bridges-ui/CarlaBridgeFormat.cpp
  46. +1
    -0
      source/bridges-ui/CarlaBridgeToolkitNative.cpp
  47. +64
    -0
      source/carla-rest-frontend
  48. +0
    -1
      source/carla_backend.pro
  49. +29
    -3
      source/carla_backend.py
  50. +568
    -0
      source/carla_backend_qtweb.py
  51. +215
    -194
      source/carla_database.py
  52. +18
    -30
      source/carla_host.py
  53. +38
    -38
      source/carla_settings.py
  54. +1
    -1
      source/carla_shared.py
  55. +6
    -0
      source/carla_skin.py
  56. +9
    -0
      source/carla_utils.py
  57. +3
    -12
      source/discovery/Makefile
  58. +65
    -162
      source/discovery/carla-discovery.cpp
  59. +2
    -1
      source/externalui.py
  60. +5
    -0
      source/libjack/libjack.cpp
  61. +1
    -1
      source/modules/dgl/src/NanoVG.cpp
  62. +1
    -0
      source/modules/dgl/src/Window.cpp
  63. +2
    -2
      source/modules/dgl/src/nanovg/fontstash.h
  64. +4
    -2
      source/modules/rtaudio/RtAudio.cpp
  65. +1
    -1
      source/modules/sfzero/sfzero/SFZReader.cpp
  66. +2
    -2
      source/modules/sfzero/sfzero/SFZSound.cpp
  67. +10
    -0
      source/modules/water/common.hpp
  68. +1
    -0
      source/modules/water/containers/Array.h
  69. +63
    -67
      source/modules/water/files/File.cpp
  70. +3
    -3
      source/modules/water/water.files.cpp
  71. +4
    -6
      source/native-plugins/_all.all.c
  72. +2
    -4
      source/native-plugins/_all.base.c
  73. +2
    -2
      source/native-plugins/_data.base.cpp
  74. +2
    -2
      source/native-plugins/audio-base.hpp
  75. +3
    -0
      source/native-plugins/resources/carla-plugin
  76. +109
    -255
      source/patchcanvas.py
  77. +8
    -8
      source/patchcanvas_theme.py
  78. +29
    -14
      source/plugin/Makefile
  79. +1
    -15
      source/plugin/carla-base.cpp
  80. +85
    -14
      source/plugin/carla-lv2-export.cpp
  81. +440
    -0
      source/plugin/carla-lv2-ui.cpp
  82. +128
    -14
      source/plugin/carla-lv2.cpp
  83. +63
    -0
      source/rest/Makefile
  84. +312
    -0
      source/rest/buffers.cpp
  85. +58
    -0
      source/rest/buffers.hpp
  86. +1228
    -0
      source/rest/carla-host.cpp
  87. +89
    -0
      source/rest/carla-utils.cpp
  88. +40
    -0
      source/rest/common.hpp
  89. +247
    -0
      source/rest/rest-server.cpp
  90. +4
    -4
      source/utils/CarlaBackendUtils.hpp
  91. +23
    -0
      source/utils/CarlaBinaryUtils.hpp
  92. +4
    -3
      source/utils/CarlaBridgeDefines.hpp
  93. +3
    -0
      source/utils/CarlaLibCounter.hpp
  94. +57
    -29
      source/utils/CarlaLv2Utils.hpp
  95. +72
    -0
      source/utils/CarlaMacUtils.cpp
  96. +52
    -0
      source/utils/CarlaMacUtils.hpp
  97. +6
    -2
      source/utils/CarlaPatchbayUtils.hpp
  98. +26
    -4
      source/utils/CarlaPipeUtils.cpp
  99. +73
    -31
      source/utils/CarlaPluginUI.cpp
  100. +1
    -1
      source/utils/CarlaPluginUI.hpp

+ 12
- 5
.gitignore View File

@@ -93,12 +93,8 @@ carla-discovery-native
carla-discovery-posix32
carla-discovery-posix64

carla-frontend
carla-lv2-export

zita-at1-ui
zita-bls1-ui
zita-rev1-ui
carla-rest-server
zynaddsubfx-ui

stoat-output.png
@@ -152,6 +148,17 @@ source/includes/rewire/
source/includes/vst2
source/includes/vst3
source/includes/config.h
source/modules/dgl/src/Resources.cpp
source/modules/dgl/src/Resources.hpp
source/modules/dgl/src/resources/
source/modules/dgl/src/sofd/
source/modules/distrho/src/DistrhoPluginJack.cpp
source/modules/distrho/src/DistrhoPluginLADSPA+DSSI.cpp
source/modules/distrho/src/DistrhoPluginLV2.cpp
source/modules/distrho/src/DistrhoPluginLV2export.cpp
source/modules/distrho/src/DistrhoPluginVST.cpp
source/modules/distrho/src/DistrhoUIDSSI.cpp
source/modules/distrho/src/DistrhoUILV2.cpp

data/linux/*.gz
data/linux/*.tgz


+ 6
- 3
Makefile View File

@@ -163,6 +163,9 @@ libjack: libs
plugin: backend bridges-plugin bridges-ui discovery
@$(MAKE) -C source/plugin

rest: libs
@$(MAKE) -C source/rest

theme: libs
@$(MAKE) -C source/theme

@@ -900,7 +903,7 @@ endif
ifeq ($(HAVE_FLUIDSYNTH),true)
@printf -- "SF2/3: $(ANS_YES)\n"
else
@printf -- "SF2/3: $(ANS_NO) $(mS)FluidSynth missing$(mE)\n"
@printf -- "SF2/3: $(ANS_NO) $(mS)FluidSynth missing$(mE)\n"
endif
@printf -- "SFZ: $(ANS_YES)\n"
@printf -- "\n"
@@ -911,8 +914,8 @@ ifneq ($(WIN32),true)
@printf -- "Carla-Patchbay: $(ANS_YES)\n"
@printf -- "Carla-Rack: $(ANS_YES)\n"
else
@printf -- "Carla-Patchbay: $(ANS_NO) $(mS)Not available for Windows$(mE)\n"
@printf -- "Carla-Rack: $(ANS_NO) $(mS)Not available for Windows$(mE)\n"
@printf -- "Carla-Patchbay: $(ANS_NO) $(mS)Not available for Windows$(mE)\n"
@printf -- "Carla-Rack: $(ANS_NO) $(mS)Not available for Windows$(mE)\n"
endif
ifeq ($(EXTERNAL_PLUGINS),true)
@printf -- "External Plugins: $(ANS_YES)\n"


+ 19
- 0
data/macos/mac-build-test.sh View File

@@ -0,0 +1,19 @@
#!/bin/bash

set -e

export MACOS_OLD=true
export CROSS_COMPILING=true

_FLAGS="-mmacosx-version-min=10.6 -Wno-attributes -Wno-deprecated-declarations -Werror"

export CFLAGS="${_FLAGS} -m32"
export CXXFLAGS="${_FLAGS} -m32"
export LDFLAGS="-m32"
apple-cross-setup make -j4

export CFLAGS="${_FLAGS} -m64"
export CXXFLAGS="${_FLAGS} -m64"
export LDFLAGS="-m64"
# FIXME
apple-cross-setup make posix64 -j4

+ 31
- 0
data/valgrind.sh View File

@@ -0,0 +1,31 @@
#!/bin/bash

export CARLA_VALGRIND_TEST=1
export WINEDEBUG=-all

# export CARLA_DEV=1
# export PYTHONMALLOC=malloc
# export SKIP_STRIPPING=true

valgrind \
--tool=memcheck \
--leak-check=full \
--show-leak-kinds=all \
--track-origins=yes \
--gen-suppressions=all \
--suppressions=./data/valgrind.supp \
-- ./bin/carla-bridge-native internal "" carlapatchbay &
PID=$!

while true; do
if jack_lsp | grep -q Carla-Patchbay:output_1; then
jack_connect Carla-Patchbay:output_2 system:playback_2
jack_connect Carla-Patchbay:output_1 system:playback_1
# jack_connect Carla-Patchbay:events-out "a2j:ZynAddSubFX [129] (playback): ZynAddSubFX"
break
else
sleep 1
fi
done

wait ${PID}

+ 56
- 0
data/valgrind.supp View File

@@ -0,0 +1,56 @@
# ld related errors, unfixed since the dawn of time
{
_dl_init-1
Memcheck:Leak
match-leak-kinds: reachable
...
fun:call_init
fun:_dl_init
obj:/lib/x86_64-linux-gnu/ld-2.27.so
}
{
_dl_init-2
Memcheck:Leak
match-leak-kinds: possible
...
fun:call_init
fun:_dl_init
obj:/lib/x86_64-linux-gnu/ld-2.27.so
}
{
_dl_init-3
Memcheck:Leak
match-leak-kinds: definite
...
fun:call_init
fun:_dl_init
obj:/lib/x86_64-linux-gnu/ld-2.27.so
}
{
dlclose-1
Memcheck:Leak
match-leak-kinds: reachable
fun:calloc
fun:_dlerror_run
fun:dlopen@@GLIBC_2.2.5
...
}
{
dlclose-2
Memcheck:Leak
match-leak-kinds: reachable
...
fun:_dl_open
...
}


# XInitThreads
{
XInitThreads
Memcheck:Leak
match-leak-kinds: reachable
fun:malloc
fun:XInitThreads
fun:main
}

+ 2
- 2
source/backend/CarlaBackend.doxygen View File

@@ -243,8 +243,8 @@ INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
PREDEFINED = DOXYGEN \
BUILDING_CARLA REAL_BUILD \
HAVE_DGL HAVE_LIBLO HAVE_LIBMAGIC HAVE_FLUIDSYNTH HAVE_LINUXSAMPLER HAVE_PROJECTM HAVE_X11 \
HAVE_EXPERIMENTAL_PLUGINS HAVE_ZYN_DEPS HAVE_ZYN_UI_DEPS
HAVE_DGL HAVE_LIBLO HAVE_LIBMAGIC HAVE_FLUIDSYNTH HAVE_PROJECTM HAVE_X11 \
HAVE_ZYN_DEPS HAVE_ZYN_UI_DEPS
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------


+ 4
- 4
source/backend/CarlaBackend.h View File

@@ -607,7 +607,7 @@ typedef enum {
*/
PARAMETER_NULL = -1,

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
/*!
* Active parameter, boolean type.
* Default is 'false'.
@@ -716,7 +716,7 @@ typedef enum {
*/
ENGINE_CALLBACK_PARAMETER_DEFAULT_CHANGED = 6,

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
/*!
* A parameter's MIDI CC has changed.
* @a pluginId Plugin Id
@@ -814,7 +814,7 @@ typedef enum {
*/
ENGINE_CALLBACK_RELOAD_ALL = 19,

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
/*!
* A patchbay client has been added.
* @a pluginId Client Id
@@ -1087,7 +1087,7 @@ typedef enum {
*/
ENGINE_OPTION_FRONTEND_WIN_ID = 17,

#ifndef CARLA_OS_WIN
#if !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH) && !defined(CARLA_OS_WIN)
/*!
* Set path to wine executable.
*/


+ 16
- 6
source/backend/CarlaEngine.hpp View File

@@ -291,6 +291,7 @@ struct CARLA_API EngineTimeInfoBBT {

#ifndef DOXYGEN
EngineTimeInfoBBT() noexcept;
EngineTimeInfoBBT(const EngineTimeInfoBBT&) noexcept;
#endif
};

@@ -310,6 +311,8 @@ struct CARLA_API EngineTimeInfo {

#ifndef DOXYGEN
EngineTimeInfo() noexcept;
EngineTimeInfo(const EngineTimeInfo&) noexcept;
EngineTimeInfo& operator=(const EngineTimeInfo&) noexcept;

// quick operator, doesn't check all values
bool operator==(const EngineTimeInfo& timeInfo) const noexcept;
@@ -821,7 +824,7 @@ public:
*/
bool removeAllPlugins();

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
/*!
* Rename plugin with id @a id to @a newName.
* Returns the new name, or null if the operation failed.
@@ -922,7 +925,7 @@ public:
/*!
* Get the current Time information (read-only).
*/
const EngineTimeInfo& getTimeInfo() const noexcept;
virtual EngineTimeInfo getTimeInfo() const noexcept;

// -------------------------------------------------------------------
// Information (peaks)
@@ -965,7 +968,7 @@ public:
*/
void setFileCallback(const FileCallbackFunc func, void* const ptr) noexcept;

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// -------------------------------------------------------------------
// Patchbay

@@ -1036,6 +1039,13 @@ public:
*/
bool setAboutToClose() noexcept;

#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
/*!
* TODO.
*/
bool isLoadingProject() const noexcept;
#endif

// -------------------------------------------------------------------
// Options

@@ -1052,7 +1062,6 @@ public:
* Check if OSC controller is registered.
*/
bool isOscControlRegistered() const noexcept;
#endif

/*!
* Idle OSC.
@@ -1068,6 +1077,7 @@ public:
* Get OSC UDP server path.
*/
const char* getOscServerPathUDP() const noexcept;
#endif

// -------------------------------------------------------------------
// Helper functions
@@ -1078,7 +1088,7 @@ public:
*/
EngineEvent* getInternalEventBuffer(const bool isInput) const noexcept;

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
/*!
* Virtual functions for handling external graph ports.
*/
@@ -1142,7 +1152,7 @@ protected:
*/
bool loadProjectInternal(water::XmlDocument& xmlDoc);

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// -------------------------------------------------------------------
// Patchbay stuff



+ 10
- 1
source/backend/CarlaHost.h View File

@@ -612,13 +612,22 @@ CARLA_EXPORT const ParameterRanges* carla_get_parameter_ranges(uint pluginId, ui
CARLA_EXPORT const MidiProgramData* carla_get_midi_program_data(uint pluginId, uint32_t midiProgramId);

/*!
* Get a plugin's custom data.
* Get a plugin's custom data, using index.
* @param pluginId Plugin
* @param customDataId Custom data index
* @see carla_get_custom_data_count()
*/
CARLA_EXPORT const CustomData* carla_get_custom_data(uint pluginId, uint32_t customDataId);

/*!
* Get a plugin's custom data value, using type and key.
* @param pluginId Plugin
* @param type Custom data type
* @param key Custom data key
* @see carla_get_custom_data_count()
*/
CARLA_EXPORT const char* carla_get_custom_data_value(uint pluginId, const char* type, const char* key);

/*!
* Get a plugin's chunk data.
* @param pluginId Plugin


+ 2
- 2
source/backend/CarlaHostCommon.cpp View File

@@ -106,7 +106,7 @@ const char* carla_get_library_filename()

if (ret.isEmpty())
{
using namespace water;
using water::File;
ret = File(File::getSpecialLocation(File::currentExecutableFile)).getFullPathName().toRawUTF8();
}

@@ -121,7 +121,7 @@ const char* carla_get_library_folder()

if (ret.isEmpty())
{
using namespace water;
using water::File;
ret = File(File::getSpecialLocation(File::currentExecutableFile).getParentDirectory()).getFullPathName().toRawUTF8();
}



+ 2
- 2
source/backend/CarlaPlugin.hpp View File

@@ -463,7 +463,7 @@ public:
*/
void setActive(const bool active, const bool sendOsc, const bool sendCallback) noexcept;

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
/*!
* Set the plugin's dry/wet signal value to @a value.
* @a value must be between 0.0 and 1.0.
@@ -749,7 +749,7 @@ public:
*/
void sendMidiSingleNote(const uint8_t channel, const uint8_t note, const uint8_t velo, const bool sendGui, const bool sendOsc, const bool sendCallback);

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
/*!
* Send all midi notes off to the host callback.
* This doesn't send the actual MIDI All-Notes-Off event, but 128 note-offs instead (IFF ctrlChannel is valid).


+ 90
- 15
source/backend/CarlaStandalone.cpp View File

@@ -98,39 +98,73 @@ typedef void (*void_func)(void);
class ThreadSafeFFTW
{
public:
struct Deinitializer {
Deinitializer(ThreadSafeFFTW& s)
: tsfftw(s) {}

~Deinitializer()
{
tsfftw.deinit();
}

ThreadSafeFFTW& tsfftw;
};

ThreadSafeFFTW()
: libfftw3(lib_open("libfftw3_threads.so.3")),
libfftw3f(lib_open("libfftw3f_threads.so.3")),
libfftw3l(lib_open("libfftw3l_threads.so.3")),
libfftw3q(lib_open("libfftw3q_threads.so.3"))
: libfftw3(nullptr),
libfftw3f(nullptr),
libfftw3l(nullptr),
libfftw3q(nullptr) {}

~ThreadSafeFFTW()
{
if (libfftw3 != nullptr)
CARLA_SAFE_ASSERT(libfftw3 == nullptr);
}

void init()
{
if ((libfftw3 = lib_open("libfftw3_threads.so.3")) != nullptr)
if (const void_func func = lib_symbol<void_func>(libfftw3, "fftw_make_planner_thread_safe"))
func();

if (libfftw3f != nullptr)
if ((libfftw3f = lib_open("libfftw3f_threads.so.3")) != nullptr)
if (const void_func func = lib_symbol<void_func>(libfftw3f, "fftwf_make_planner_thread_safe"))
func();

if (libfftw3l != nullptr)
if ((libfftw3l = lib_open("libfftw3l_threads.so.3")) != nullptr)
if (const void_func func = lib_symbol<void_func>(libfftw3l, "fftwl_make_planner_thread_safe"))
func();

if (libfftw3q != nullptr)
if ((libfftw3q = lib_open("libfftw3q_threads.so.3")) != nullptr)
if (const void_func func = lib_symbol<void_func>(libfftw3q, "fftwq_make_planner_thread_safe"))
func();
}

~ThreadSafeFFTW()
void deinit()
{
if (libfftw3 != nullptr)
{
lib_close(libfftw3);
libfftw3 = nullptr;
}

if (libfftw3f != nullptr)
{
lib_close(libfftw3f);
libfftw3f = nullptr;
}

if (libfftw3l != nullptr)
{
lib_close(libfftw3l);
libfftw3l = nullptr;
}

if (libfftw3q != nullptr)
{
lib_close(libfftw3q);
libfftw3q = nullptr;
}
}

private:
@@ -139,6 +173,8 @@ private:
lib_t libfftw3l;
lib_t libfftw3q;
};

static ThreadSafeFFTW sThreadSafeFFTW;
#endif

// --------------------------------------------------------------------------------------------------------------------
@@ -173,7 +209,7 @@ const char* const* carla_get_engine_driver_device_names(uint index)

const EngineDriverDeviceInfo* carla_get_engine_driver_device_info(uint index, const char* name)
{
CARLA_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', nullptr);
CARLA_SAFE_ASSERT_RETURN(name != nullptr, nullptr);

static EngineDriverDeviceInfo retDevInfo;
static const uint32_t nullBufferSizes[] = { 0 };
@@ -349,9 +385,6 @@ bool carla_engine_init(const char* driverName, const char* clientName)
#ifdef CARLA_OS_WIN
carla_setenv("WINEASIO_CLIENT_NAME", clientName);
#endif
#ifdef CARLA_OS_UNIX
static const ThreadSafeFFTW tsfftw;
#endif

ScopedPointer<CarlaEngine> engine(CarlaEngine::newDriverByName(driverName));

@@ -375,6 +408,9 @@ bool carla_engine_init(const char* driverName, const char* clientName)
#ifndef BUILD_BRIDGE
if (gStandalone.logThreadEnabled && std::getenv("CARLA_LOGS_DISABLED") == nullptr)
gStandalone.logThread.init();
#endif
#ifdef CARLA_OS_UNIX
sThreadSafeFFTW.init();
#endif
gStandalone.lastError = "No error";
gStandalone.engine = engine.release();
@@ -432,6 +468,10 @@ bool carla_engine_close()

CARLA_SAFE_ASSERT_WITH_LAST_ERROR_RETURN(gStandalone.engine != nullptr, "Engine is not initialized", false);

#ifdef CARLA_OS_UNIX
const ThreadSafeFFTW::Deinitializer tsfftwde(sThreadSafeFFTW);
#endif

ScopedPointer<CarlaEngine> engine(gStandalone.engine);
gStandalone.engine = nullptr;

@@ -1324,6 +1364,40 @@ const CustomData* carla_get_custom_data(uint pluginId, uint32_t customDataId)
return &retCustomData;
}

const char* carla_get_custom_data_value(uint pluginId, const char* type, const char* key)
{
CARLA_SAFE_ASSERT_RETURN(type != nullptr && type[0] != '\0', gNullCharPtr);
CARLA_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0', gNullCharPtr);
CARLA_SAFE_ASSERT_RETURN(gStandalone.engine != nullptr, gNullCharPtr);

CarlaPlugin* const plugin(gStandalone.engine->getPlugin(pluginId));
CARLA_SAFE_ASSERT_RETURN(plugin != nullptr, gNullCharPtr);

carla_debug("carla_get_custom_data_value(%i, %s, %s)", pluginId, type, key);

const uint32_t count = plugin->getCustomDataCount();

if (count == 0)
return gNullCharPtr;

static CarlaString customDataValue;

for (uint32_t i=0; i<count; ++i)
{
const CustomData& pluginCustomData(plugin->getCustomData(i));

if (std::strcmp(pluginCustomData.type, type) != 0)
continue;
if (std::strcmp(pluginCustomData.key, key) != 0)
continue;

customDataValue = pluginCustomData.value;
return customDataValue.buffer();
}

return gNullCharPtr;
}

const char* carla_get_chunk_data(uint pluginId)
{
CARLA_SAFE_ASSERT_RETURN(gStandalone.engine != nullptr, gNullCharPtr);
@@ -1856,7 +1930,7 @@ const char* carla_get_host_osc_url_tcp()
{
carla_debug("carla_get_host_osc_url_tcp()");

#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
if (gStandalone.engine == nullptr)
{
carla_stderr2("carla_get_host_osc_url_tcp() failed, engine is not running");
@@ -1874,7 +1948,7 @@ const char* carla_get_host_osc_url_udp()
{
carla_debug("carla_get_host_osc_url_udp()");

#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
if (gStandalone.engine == nullptr)
{
carla_stderr2("carla_get_host_osc_url_udp() failed, engine is not running");
@@ -1895,6 +1969,7 @@ const char* carla_get_host_osc_url_udp()
#undef CARLA_PLUGIN_UI_CLASS_PREFIX

#include "CarlaDssiUtils.cpp"
#include "CarlaMacUtils.cpp"
#include "CarlaPatchbayUtils.cpp"
#include "CarlaPipeUtils.cpp"
#include "CarlaStateUtils.cpp"


+ 0
- 837
source/backend/CarlaUtils.cpp View File

@@ -1,837 +0,0 @@
/*
* Carla Plugin Host
* Copyright (C) 2011-2018 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

#include "CarlaUtils.h"

#include "CarlaNative.h"

#include "CarlaBackendUtils.hpp"
#include "CarlaLv2Utils.hpp"
#include "CarlaPipeUtils.hpp"
#include "CarlaThread.hpp"
#include "LinkedList.hpp"

#include "water/files/File.h"

#ifdef CARLA_OS_MAC
# import <Cocoa/Cocoa.h>
#endif

#ifndef CARLA_UTILS_CACHED_PLUGINS_ONLY
# include "rtaudio/RtAudio.h"
# include "rtmidi/RtMidi.h"
# ifdef HAVE_X11
# include <X11/Xlib.h>
# endif
#endif

#include "../native-plugins/_data.all.cpp"

namespace CB = CarlaBackend;

static const char* const gNullCharPtr = "";

// -------------------------------------------------------------------------------------------------------------------

_CarlaCachedPluginInfo::_CarlaCachedPluginInfo() noexcept
: category(CB::PLUGIN_CATEGORY_NONE),
hints(0x0),
audioIns(0),
audioOuts(0),
midiIns(0),
midiOuts(0),
parameterIns(0),
parameterOuts(0),
name(gNullCharPtr),
label(gNullCharPtr),
maker(gNullCharPtr),
copyright(gNullCharPtr) {}

// -------------------------------------------------------------------------------------------------------------------

uint carla_get_cached_plugin_count(CB::PluginType ptype, const char* pluginPath)
{
CARLA_SAFE_ASSERT_RETURN(ptype == CB::PLUGIN_INTERNAL || ptype == CB::PLUGIN_LV2, 0);
carla_debug("carla_get_cached_plugin_count(%i:%s)", ptype, CB::PluginType2Str(ptype));

switch (ptype)
{
case CB::PLUGIN_INTERNAL: {
uint32_t count = 0;
carla_get_native_plugins_data(&count);
return count;
}

case CB::PLUGIN_LV2: {
Lv2WorldClass& lv2World(Lv2WorldClass::getInstance());
lv2World.initIfNeeded(pluginPath);
return lv2World.getPluginCount();
}

default:
return 0;
}
}

const CarlaCachedPluginInfo* carla_get_cached_plugin_info(CB::PluginType ptype, uint index)
{
carla_debug("carla_get_cached_plugin_info(%i:%s, %i)", ptype, CB::PluginType2Str(ptype), index);

static CarlaCachedPluginInfo info;

switch (ptype)
{
case CB::PLUGIN_INTERNAL: {
uint32_t count = 0;
const NativePluginDescriptor* const descs(carla_get_native_plugins_data(&count));
CARLA_SAFE_ASSERT_BREAK(index < count);
CARLA_SAFE_ASSERT_BREAK(descs != nullptr);

const NativePluginDescriptor& desc(descs[index]);

info.category = static_cast<CB::PluginCategory>(desc.category);
info.hints = 0x0;

if (desc.hints & NATIVE_PLUGIN_IS_RTSAFE)
info.hints |= CB::PLUGIN_IS_RTSAFE;
if (desc.hints & NATIVE_PLUGIN_IS_SYNTH)
info.hints |= CB::PLUGIN_IS_SYNTH;
if (desc.hints & NATIVE_PLUGIN_HAS_UI)
info.hints |= CB::PLUGIN_HAS_CUSTOM_UI;
if (desc.hints & NATIVE_PLUGIN_NEEDS_FIXED_BUFFERS)
info.hints |= CB::PLUGIN_NEEDS_FIXED_BUFFERS;
if (desc.hints & NATIVE_PLUGIN_NEEDS_UI_MAIN_THREAD)
info.hints |= CB::PLUGIN_NEEDS_UI_MAIN_THREAD;
if (desc.hints & NATIVE_PLUGIN_USES_MULTI_PROGS)
info.hints |= CB::PLUGIN_USES_MULTI_PROGS;

info.audioIns = desc.audioIns;
info.audioOuts = desc.audioOuts;
info.midiIns = desc.midiIns;
info.midiOuts = desc.midiOuts;
info.parameterIns = desc.paramIns;
info.parameterOuts = desc.paramOuts;
info.name = desc.name;
info.label = desc.label;
info.maker = desc.maker;
info.copyright = desc.copyright;
return &info;
}

case CB::PLUGIN_LV2: {
Lv2WorldClass& lv2World(Lv2WorldClass::getInstance());

const LilvPlugin* const cPlugin(lv2World.getPluginFromIndex(index));
CARLA_SAFE_ASSERT_BREAK(cPlugin != nullptr);

Lilv::Plugin lilvPlugin(cPlugin);
CARLA_SAFE_ASSERT_BREAK(lilvPlugin.get_uri().is_uri());

// features
info.hints = 0x0;

if (lilvPlugin.get_uis().size() > 0)
info.hints |= CB::PLUGIN_HAS_CUSTOM_UI;

{
Lilv::Nodes lilvFeatureNodes(lilvPlugin.get_supported_features());

LILV_FOREACH(nodes, it, lilvFeatureNodes)
{
Lilv::Node lilvFeatureNode(lilvFeatureNodes.get(it));
const char* const featureURI(lilvFeatureNode.as_uri());
CARLA_SAFE_ASSERT_CONTINUE(featureURI != nullptr);

if (std::strcmp(featureURI, LV2_CORE__hardRTCapable) == 0)
info.hints |= CB::PLUGIN_IS_RTSAFE;
}

lilv_nodes_free(const_cast<LilvNodes*>(lilvFeatureNodes.me));
}

// category
info.category = CB::PLUGIN_CATEGORY_NONE;

{
Lilv::Nodes typeNodes(lilvPlugin.get_value(lv2World.rdf_type));

if (typeNodes.size() > 0)
{
if (typeNodes.contains(lv2World.class_allpass))
info.category = CB::PLUGIN_CATEGORY_FILTER;
if (typeNodes.contains(lv2World.class_amplifier))
info.category = CB::PLUGIN_CATEGORY_DYNAMICS;
if (typeNodes.contains(lv2World.class_analyzer))
info.category = CB::PLUGIN_CATEGORY_UTILITY;
if (typeNodes.contains(lv2World.class_bandpass))
info.category = CB::PLUGIN_CATEGORY_FILTER;
if (typeNodes.contains(lv2World.class_chorus))
info.category = CB::PLUGIN_CATEGORY_MODULATOR;
if (typeNodes.contains(lv2World.class_comb))
info.category = CB::PLUGIN_CATEGORY_FILTER;
if (typeNodes.contains(lv2World.class_compressor))
info.category = CB::PLUGIN_CATEGORY_DYNAMICS;
if (typeNodes.contains(lv2World.class_constant))
info.category = CB::PLUGIN_CATEGORY_OTHER;
if (typeNodes.contains(lv2World.class_converter))
info.category = CB::PLUGIN_CATEGORY_UTILITY;
if (typeNodes.contains(lv2World.class_delay))
info.category = CB::PLUGIN_CATEGORY_DELAY;
if (typeNodes.contains(lv2World.class_distortion))
info.category = CB::PLUGIN_CATEGORY_DISTORTION;
if (typeNodes.contains(lv2World.class_dynamics))
info.category = CB::PLUGIN_CATEGORY_DYNAMICS;
if (typeNodes.contains(lv2World.class_eq))
info.category = CB::PLUGIN_CATEGORY_EQ;
if (typeNodes.contains(lv2World.class_envelope))
info.category = CB::PLUGIN_CATEGORY_DYNAMICS;
if (typeNodes.contains(lv2World.class_expander))
info.category = CB::PLUGIN_CATEGORY_DYNAMICS;
if (typeNodes.contains(lv2World.class_filter))
info.category = CB::PLUGIN_CATEGORY_FILTER;
if (typeNodes.contains(lv2World.class_flanger))
info.category = CB::PLUGIN_CATEGORY_MODULATOR;
if (typeNodes.contains(lv2World.class_function))
info.category = CB::PLUGIN_CATEGORY_UTILITY;
if (typeNodes.contains(lv2World.class_gate))
info.category = CB::PLUGIN_CATEGORY_DYNAMICS;
if (typeNodes.contains(lv2World.class_generator))
info.category = CB::PLUGIN_CATEGORY_OTHER;
if (typeNodes.contains(lv2World.class_highpass))
info.category = CB::PLUGIN_CATEGORY_FILTER;
if (typeNodes.contains(lv2World.class_limiter))
info.category = CB::PLUGIN_CATEGORY_DYNAMICS;
if (typeNodes.contains(lv2World.class_lowpass))
info.category = CB::PLUGIN_CATEGORY_FILTER;
if (typeNodes.contains(lv2World.class_mixer))
info.category = CB::PLUGIN_CATEGORY_UTILITY;
if (typeNodes.contains(lv2World.class_modulator))
info.category = CB::PLUGIN_CATEGORY_MODULATOR;
if (typeNodes.contains(lv2World.class_multiEQ))
info.category = CB::PLUGIN_CATEGORY_EQ;
if (typeNodes.contains(lv2World.class_oscillator))
info.category = CB::PLUGIN_CATEGORY_OTHER;
if (typeNodes.contains(lv2World.class_paraEQ))
info.category = CB::PLUGIN_CATEGORY_EQ;
if (typeNodes.contains(lv2World.class_phaser))
info.category = CB::PLUGIN_CATEGORY_MODULATOR;
if (typeNodes.contains(lv2World.class_pitch))
info.category = CB::PLUGIN_CATEGORY_OTHER;
if (typeNodes.contains(lv2World.class_reverb))
info.category = CB::PLUGIN_CATEGORY_DELAY;
if (typeNodes.contains(lv2World.class_simulator))
info.category = CB::PLUGIN_CATEGORY_OTHER;
if (typeNodes.contains(lv2World.class_spatial))
info.category = CB::PLUGIN_CATEGORY_OTHER;
if (typeNodes.contains(lv2World.class_spectral))
info.category = CB::PLUGIN_CATEGORY_OTHER;
if (typeNodes.contains(lv2World.class_utility))
info.category = CB::PLUGIN_CATEGORY_UTILITY;
if (typeNodes.contains(lv2World.class_waveshaper))
info.category = CB::PLUGIN_CATEGORY_DISTORTION;
if (typeNodes.contains(lv2World.class_instrument))
{
info.category = CB::PLUGIN_CATEGORY_SYNTH;
info.hints |= CB::PLUGIN_IS_SYNTH;
}
}

lilv_nodes_free(const_cast<LilvNodes*>(typeNodes.me));
}

// number data
info.audioIns = 0;
info.audioOuts = 0;
info.midiIns = 0;
info.midiOuts = 0;
info.parameterIns = 0;
info.parameterOuts = 0;

for (uint i=0, count=lilvPlugin.get_num_ports(); i<count; ++i)
{
Lilv::Port lilvPort(lilvPlugin.get_port_by_index(i));

bool isInput;

/**/ if (lilvPort.is_a(lv2World.port_input))
isInput = true;
else if (lilvPort.is_a(lv2World.port_output))
isInput = false;
else
continue;

/**/ if (lilvPort.is_a(lv2World.port_control))
{
// skip some control ports
if (lilvPort.has_property(lv2World.reportsLatency))
continue;

if (LilvNode* const designationNode = lilv_port_get(lilvPort.parent, lilvPort.me, lv2World.designation.me))
{
bool skip = false;

if (const char* const designation = lilv_node_as_string(designationNode))
{
/**/ if (std::strcmp(designation, LV2_CORE__control) == 0)
skip = true;
else if (std::strcmp(designation, LV2_CORE__freeWheeling) == 0)
skip = true;
else if (std::strcmp(designation, LV2_CORE__latency) == 0)
skip = true;
else if (std::strcmp(designation, LV2_PARAMETERS__sampleRate) == 0)
skip = true;
else if (std::strcmp(designation, LV2_TIME__bar) == 0)
skip = true;
else if (std::strcmp(designation, LV2_TIME__barBeat) == 0)
skip = true;
else if (std::strcmp(designation, LV2_TIME__beat) == 0)
skip = true;
else if (std::strcmp(designation, LV2_TIME__beatUnit) == 0)
skip = true;
else if (std::strcmp(designation, LV2_TIME__beatsPerBar) == 0)
skip = true;
else if (std::strcmp(designation, LV2_TIME__beatsPerMinute) == 0)
skip = true;
else if (std::strcmp(designation, LV2_TIME__frame) == 0)
skip = true;
else if (std::strcmp(designation, LV2_TIME__framesPerSecond) == 0)
skip = true;
else if (std::strcmp(designation, LV2_TIME__speed) == 0)
skip = true;
else if (std::strcmp(designation, LV2_KXSTUDIO_PROPERTIES__TimePositionTicksPerBeat) == 0)
skip = true;
}

lilv_node_free(designationNode);

if (skip)
continue;
}

if (isInput)
++(info.parameterIns);
else
++(info.parameterOuts);
}
else if (lilvPort.is_a(lv2World.port_audio))
{
if (isInput)
++(info.audioIns);
else
++(info.audioOuts);
}
else if (lilvPort.is_a(lv2World.port_cv))
{
}
else if (lilvPort.is_a(lv2World.port_atom))
{
Lilv::Nodes supportNodes(lilvPort.get_value(lv2World.atom_supports));

for (LilvIter *it = lilv_nodes_begin(supportNodes.me); ! lilv_nodes_is_end(supportNodes.me, it); it = lilv_nodes_next(supportNodes.me, it))
{
const Lilv::Node node(lilv_nodes_get(supportNodes.me, it));
CARLA_SAFE_ASSERT_CONTINUE(node.is_uri());

if (node.equals(lv2World.midi_event))
{
if (isInput)
++(info.midiIns);
else
++(info.midiOuts);
}
}

lilv_nodes_free(const_cast<LilvNodes*>(supportNodes.me));
}
else if (lilvPort.is_a(lv2World.port_event))
{
if (lilvPort.supports_event(lv2World.midi_event))
{
if (isInput)
++(info.midiIns);
else
++(info.midiOuts);
}
}
else if (lilvPort.is_a(lv2World.port_midi))
{
if (isInput)
++(info.midiIns);
else
++(info.midiOuts);
}
}

// text data
static CarlaString suri, sname, smaker, slicense;
suri.clear(); sname.clear(); smaker.clear(); slicense.clear();

suri = lilvPlugin.get_uri().as_uri();

if (LilvNode* const nameNode = lilv_plugin_get_name(lilvPlugin.me))
{
if (const char* const name = lilv_node_as_string(nameNode))
sname = name;
lilv_node_free(nameNode);
}

if (const char* const author = lilvPlugin.get_author_name().as_string())
smaker = author;

Lilv::Nodes licenseNodes(lilvPlugin.get_value(lv2World.doap_license));

if (licenseNodes.size() > 0)
{
if (const char* const license = licenseNodes.get_first().as_string())
slicense = license;
}

lilv_nodes_free(const_cast<LilvNodes*>(licenseNodes.me));

info.name = sname;
info.label = suri;
info.maker = smaker;
info.copyright = slicense;

return &info;
}

default:
break;
}

info.category = CB::PLUGIN_CATEGORY_NONE;
info.hints = 0x0;
info.audioIns = 0;
info.audioOuts = 0;
info.midiIns = 0;
info.midiOuts = 0;
info.parameterIns = 0;
info.parameterOuts = 0;
info.name = gNullCharPtr;
info.label = gNullCharPtr;
info.maker = gNullCharPtr;
info.copyright = gNullCharPtr;
return &info;
}

#ifndef CARLA_UTILS_CACHED_PLUGINS_ONLY
// -------------------------------------------------------------------------------------------------------------------

const char* carla_get_complete_license_text()
{
carla_debug("carla_get_complete_license_text()");

static CarlaString retText;

if (retText.isEmpty())
{
retText =
"<p>This current Carla build is using the following features and 3rd-party code:</p>"
"<ul>"

// Plugin formats
"<li>LADSPA plugin support</li>"
"<li>DSSI plugin support</li>"
"<li>LV2 plugin support</li>"
"<li>VST2 plugin support using VeSTige header by Javier Serrano Polo</li>"

// Sample kit libraries
#ifdef HAVE_FLUIDSYNTH
"<li>FluidSynth library for SF2/3 support</li>"
#endif
"<li>SFZero module for SFZ support</li>"

// misc libs
"<li>base64 utilities based on code by Ren\u00E9 Nyffenegger</li>"
"<li>liblo library for OSC support</li>"
"<li>rtmempool library by Nedko Arnaudov"
"<li>serd, sord, sratom and lilv libraries for LV2 discovery</li>"
"<li>RtAudio v" RTAUDIO_VERSION " and RtMidi v" RTMIDI_VERSION " for native Audio and MIDI support</li>"

// Internal plugins
"<li>MIDI Sequencer UI code by Perry Nguyen</li>"

// External plugins
#ifdef HAVE_EXTERNAL_PLUGINS
"<li>Nekobi plugin code based on nekobee by Sean Bolton and others</li>"
"<li>VectorJuice and WobbleJuice plugin code by Andre Sklenar</li>"
# ifdef HAVE_ZYN_DEPS
"<li>ZynAddSubFX plugin code by Mark McCurry and Nasca Octavian Paul</li>"
# endif
#endif // HAVE_EXTERNAL_PLUGINS

// end
"</ul>";
}

return retText;
}

const char* const* carla_get_supported_file_extensions()
{
carla_debug("carla_get_supported_file_extensions()");

// NOTE: please keep in sync with CarlaEngine::loadFile!!
static const char* const extensions[] = {
// Base types
"carxp", "carxs",

// plugin files and resources
#ifdef HAVE_FLUIDSYNTH
"sf2", "sf3",
#endif
#ifdef HAVE_ZYN_DEPS
"xmz", "xiz",
#endif
#if defined(CARLA_OS_MAC)
"vst",
#else
"dll",
"so",
#endif

// Audio files
#ifdef HAVE_SNDFILE
"aif", "aifc", "aiff", "au", "bwf", "flac", "htk", "iff", "mat4", "mat5", "oga", "ogg",
"paf", "pvf", "pvf5", "sd2", "sf", "snd", "svx", "vcc", "w64", "wav", "xi",
#endif
#ifdef HAVE_FFMPEG
"3g2", "3gp", "aac", "ac3", "amr", "ape", "mp2", "mp3", "mpc", "wma",
# ifdef HAVE_SNDFILE
// FFmpeg without sndfile
"flac", "oga", "ogg", "w64", "wav",
# endif
#endif

// MIDI files
"mid", "midi",

// SFZ
"sfz",

// terminator
nullptr
};

return extensions;
}

const char* const* carla_get_supported_features()
{
carla_debug("carla_get_supported_features()");

static const char* const features[] = {
#ifdef HAVE_FLUIDSYNTH
"sf2",
#endif
#ifdef HAVE_HYLIA
"link",
#endif
#ifdef HAVE_LIBLO
"osc",
#endif
#if defined(HAVE_LIBMAGIC) || defined(CARLA_OS_WIN)
"bridges",
#endif
#ifdef HAVE_PYQT
"gui",
#endif
nullptr
};

return features;
}

// -------------------------------------------------------------------------------------------------------------------

void carla_fflush(bool err)
{
std::fflush(err ? stderr : stdout);
}

void carla_fputs(bool err, const char* string)
{
std::fputs(string, err ? stderr : stdout);
}

void carla_set_process_name(const char* name)
{
carla_debug("carla_set_process_name(\"%s\")", name);

CarlaThread::setCurrentThreadName(name);
}

// -------------------------------------------------------------------------------------------------------------------

class CarlaPipeClientPlugin : public CarlaPipeClient
{
public:
CarlaPipeClientPlugin(const CarlaPipeCallbackFunc callbackFunc, void* const callbackPtr) noexcept
: CarlaPipeClient(),
fCallbackFunc(callbackFunc),
fCallbackPtr(callbackPtr),
fLastReadLine(nullptr)
{
CARLA_SAFE_ASSERT(fCallbackFunc != nullptr);
}

~CarlaPipeClientPlugin() override
{
if (fLastReadLine != nullptr)
{
delete[] fLastReadLine;
fLastReadLine = nullptr;
}
}

const char* readlineblock(const uint timeout) noexcept
{
delete[] fLastReadLine;
fLastReadLine = CarlaPipeClient::_readlineblock(timeout);
return fLastReadLine;
}

bool msgReceived(const char* const msg) noexcept override
{
if (fCallbackFunc != nullptr)
{
try {
fCallbackFunc(fCallbackPtr, msg);
} CARLA_SAFE_EXCEPTION("msgReceived");
}

return true;
}

private:
const CarlaPipeCallbackFunc fCallbackFunc;
void* const fCallbackPtr;
const char* fLastReadLine;

CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPipeClientPlugin)
};

CarlaPipeClientHandle carla_pipe_client_new(const char* argv[], CarlaPipeCallbackFunc callbackFunc, void* callbackPtr)
{
carla_debug("carla_pipe_client_new(%p, %p, %p)", argv, callbackFunc, callbackPtr);

CarlaPipeClientPlugin* const pipe(new CarlaPipeClientPlugin(callbackFunc, callbackPtr));

if (! pipe->initPipeClient(argv))
{
delete pipe;
return nullptr;
}

return pipe;
}

void carla_pipe_client_idle(CarlaPipeClientHandle handle)
{
CARLA_SAFE_ASSERT_RETURN(handle != nullptr,);

((CarlaPipeClientPlugin*)handle)->idlePipe();
}

bool carla_pipe_client_is_running(CarlaPipeClientHandle handle)
{
CARLA_SAFE_ASSERT_RETURN(handle != nullptr, false);

return ((CarlaPipeClientPlugin*)handle)->isPipeRunning();
}

void carla_pipe_client_lock(CarlaPipeClientHandle handle)
{
CARLA_SAFE_ASSERT_RETURN(handle != nullptr,);

return ((CarlaPipeClientPlugin*)handle)->lockPipe();
}

void carla_pipe_client_unlock(CarlaPipeClientHandle handle)
{
CARLA_SAFE_ASSERT_RETURN(handle != nullptr,);

return ((CarlaPipeClientPlugin*)handle)->unlockPipe();
}

const char* carla_pipe_client_readlineblock(CarlaPipeClientHandle handle, uint timeout)
{
CARLA_SAFE_ASSERT_RETURN(handle != nullptr, nullptr);

return ((CarlaPipeClientPlugin*)handle)->readlineblock(timeout);
}

bool carla_pipe_client_write_msg(CarlaPipeClientHandle handle, const char* msg)
{
CARLA_SAFE_ASSERT_RETURN(handle != nullptr, false);

return ((CarlaPipeClientPlugin*)handle)->writeMessage(msg);
}

bool carla_pipe_client_write_and_fix_msg(CarlaPipeClientHandle handle, const char* msg)
{
CARLA_SAFE_ASSERT_RETURN(handle != nullptr, false);

return ((CarlaPipeClientPlugin*)handle)->writeAndFixMessage(msg);
}

bool carla_pipe_client_flush(CarlaPipeClientHandle handle)
{
CARLA_SAFE_ASSERT_RETURN(handle != nullptr, false);

return ((CarlaPipeClientPlugin*)handle)->flushMessages();
}

bool carla_pipe_client_flush_and_unlock(CarlaPipeClientHandle handle)
{
CARLA_SAFE_ASSERT_RETURN(handle != nullptr, false);

CarlaPipeClientPlugin* const pipe((CarlaPipeClientPlugin*)handle);
const bool ret(pipe->flushMessages());
pipe->unlockPipe();
return ret;
}

void carla_pipe_client_destroy(CarlaPipeClientHandle handle)
{
CARLA_SAFE_ASSERT_RETURN(handle != nullptr,);
carla_debug("carla_pipe_client_destroy(%p)", handle);

CarlaPipeClientPlugin* const pipe((CarlaPipeClientPlugin*)handle);
pipe->closePipeClient();
delete pipe;
}

// -------------------------------------------------------------------------------------------------------------------

const char* carla_get_library_filename()
{
carla_debug("carla_get_library_filename()");

static CarlaString ret;

if (ret.isEmpty())
{
using water::File;
ret = File(File::getSpecialLocation(File::currentExecutableFile)).getFullPathName().toRawUTF8();
}

return ret;
}

const char* carla_get_library_folder()
{
carla_debug("carla_get_library_folder()");

static CarlaString ret;

if (ret.isEmpty())
{
using water::File;
ret = File(File::getSpecialLocation(File::currentExecutableFile).getParentDirectory()).getFullPathName().toRawUTF8();
}

return ret;
}

// -------------------------------------------------------------------------------------------------------------------

void carla_x11_reparent_window(uintptr_t winId1, uintptr_t winId2)
{
CARLA_SAFE_ASSERT_RETURN(winId1 != 0,);
CARLA_SAFE_ASSERT_RETURN(winId2 != 0,);

#ifdef HAVE_X11
if (::Display* const disp = XOpenDisplay(nullptr))
{
XReparentWindow(disp, winId1, winId2, 0, 0);
XMapWindow(disp, winId1);
XCloseDisplay(disp);
}
#endif
}

void carla_x11_move_window(uintptr_t winId, int x, int y)
{
CARLA_SAFE_ASSERT_RETURN(winId != 0,);

#ifdef HAVE_X11
if (::Display* const disp = XOpenDisplay(nullptr))
{
XMoveWindow(disp, winId, x, y);
XCloseDisplay(disp);
}
#else
// unused
return; (void)x; (void)y;
#endif
}

int* carla_x11_get_window_pos(uintptr_t winId)
{
static int pos[2];

if (winId == 0)
{
pos[0] = 0;
pos[1] = 0;
}
#ifdef HAVE_X11
else if (::Display* const disp = XOpenDisplay(nullptr))
{
int x, y;
Window child;
XWindowAttributes xwa;
XTranslateCoordinates(disp, winId, XRootWindow(disp, 0), 0, 0, &x, &y, &child);
XGetWindowAttributes(disp, winId, &xwa);
XCloseDisplay(disp);
pos[0] = x - xwa.x;
pos[1] = y - xwa.y;
}
#endif
else
{
pos[0] = 0;
pos[1] = 0;
}

return pos;
}

int carla_cocoa_get_window(void* nsViewPtr)
{
CARLA_SAFE_ASSERT_RETURN(nsViewPtr != nullptr, 0);

#ifdef CARLA_OS_MAC
NSView* nsView = (NSView*)nsViewPtr;
return [[nsView window] windowNumber];
#else
return 0;
#endif
}

// -------------------------------------------------------------------------------------------------------------------

#include "CarlaPipeUtils.cpp"

#if !(defined(DEBUG) || defined(BUILDING_CARLA_FOR_WINDOWS))
# include "water/misc/Time.cpp"
#endif

// -------------------------------------------------------------------------------------------------------------------
#endif // CARLA_UTILS_CACHED_PLUGINS_ONLY

+ 9
- 1
source/backend/CarlaUtils.h View File

@@ -49,6 +49,14 @@ typedef void (*CarlaPipeCallbackFunc)(void* ptr, const char* msg);
* @see carla_get_cached_plugin_info()
*/
typedef struct _CarlaCachedPluginInfo {
/*!
* Wherever the data in this struct is valid.
* For performance reasons, plugins are only checked on request,
* and as such, the count vs number of really valid plugins might not match.
* Use this field to skip on plugins which cannot be loaded in Carla.
*/
bool valid;

/*!
* Plugin category.
*/
@@ -141,7 +149,7 @@ CARLA_EXPORT const char* const* carla_get_supported_features();

/*!
* Get how many cached plugins are available.
* Internal, LV2 and AU plugin formats are cached and need to be discovered via this function.
* Internal and LV2 plugin formats are cached and need to be discovered via this function.
* Do not call this for any other plugin formats.
*/
CARLA_EXPORT uint carla_get_cached_plugin_count(PluginType ptype, const char* pluginPath);


+ 4
- 32
source/backend/Makefile View File

@@ -13,12 +13,8 @@ OBJS_standalone = \
$(OBJDIR)/CarlaStandalone.cpp.o \
$(OBJDIR)/CarlaStandaloneNSM.cpp.o

OBJS_utils = \
$(OBJDIR)/CarlaUtils.cpp.o

TARGETS = \
$(BINDIR)/libcarla_standalone2$(LIB_EXT) \
$(BINDIR)/libcarla_utils$(LIB_EXT)
$(BINDIR)/libcarla_standalone2$(LIB_EXT)

# ----------------------------------------------------------------------------------------------------------------------------

@@ -44,9 +40,6 @@ endif
STANDALONE_LIBS += $(MODULEDIR)/rtaudio.a
STANDALONE_LIBS += $(MODULEDIR)/rtmidi.a

UTILS_LIBS += $(MODULEDIR)/lilv.a
UTILS_LIBS += $(MODULEDIR)/water.files.a

# ----------------------------------------------------------------------------------------------------------------------------

STANDALONE_LINK_FLAGS = $(JACKBRIDGE_LIBS)
@@ -61,30 +54,20 @@ STANDALONE_LINK_FLAGS += $(RTMIDI_LIBS)
STANDALONE_LINK_FLAGS += $(LIBLO_LIBS)
STANDALONE_LINK_FLAGS += $(MAGIC_LIBS)
STANDALONE_LINK_FLAGS += $(FLUIDSYNTH_LIBS)
STANDALONE_LINK_FLAGS += $(LINUXSAMPLER_LIBS)
STANDALONE_LINK_FLAGS += $(X11_LIBS)

UTILS_LINK_FLAGS += $(LILV_LIBS)
UTILS_LINK_FLAGS += $(WATER_LIBS)

ifeq ($(HAVE_X11),true)
UTILS_LINK_FLAGS += $(X11_LIBS)
endif

ifneq ($(HAIKU),true)
UTILS_LINK_FLAGS += -lpthread
endif

# ----------------------------------------------------------------------------------------------------------------------------

all: $(TARGETS)
$(MAKE) -C utils

# ----------------------------------------------------------------------------------------------------------------------------

clean:
rm -f $(OBJS_standalone) $(OBJS_utils) $(TARGETS)
rm -f $(OBJS_standalone) $(TARGETS)
$(MAKE) clean -C engine
$(MAKE) clean -C plugin
$(MAKE) clean -C utils

debug:
$(MAKE) DEBUG=true
@@ -99,11 +82,6 @@ $(BINDIR)/libcarla_standalone2$(LIB_EXT): $(OBJS_standalone) $(STANDALONE_LIBS)
@echo "Linking libcarla_standalone2$(LIB_EXT)"
@$(CXX) $(OBJS_standalone) $(LIBS_START) $(STANDALONE_LIBS) $(LIBS_END) $(LINK_FLAGS) $(STANDALONE_LINK_FLAGS) $(SHARED) -o $@

$(BINDIR)/libcarla_utils$(LIB_EXT): $(OBJS_utils) $(UTILS_LIBS)
-@mkdir -p $(BINDIR)
@echo "Linking libcarla_utils$(LIB_EXT)"
@$(CXX) $(OBJS_utils) $(LIBS_START) $(UTILS_LIBS) $(LIBS_END) $(LINK_FLAGS) $(UTILS_LINK_FLAGS) $(SHARED) -o $@

# ----------------------------------------------------------------------------------------------------------------------------

ifeq ($(MACOS),true)
@@ -111,11 +89,6 @@ $(OBJDIR)/CarlaStandalone.cpp.o: CarlaStandalone.cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $<"
@$(CXX) $< $(BUILD_CXX_FLAGS) -ObjC++ -c -o $@

$(OBJDIR)/CarlaUtils.cpp.o: CarlaUtils.cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $<"
@$(CXX) $< $(BUILD_CXX_FLAGS) -ObjC++ -c -o $@
endif

$(OBJDIR)/%.cpp.o: %.cpp
@@ -124,6 +97,5 @@ $(OBJDIR)/%.cpp.o: %.cpp
@$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@

-include $(OBJS_standalone:%.o=%.d)
-include $(OBJS_utils:%.o=%.d)

# ----------------------------------------------------------------------------------------------------------------------------

+ 71
- 48
source/backend/engine/CarlaEngine.cpp View File

@@ -118,7 +118,7 @@ const char* const* CarlaEngine::getDriverDeviceNames(const uint index2)

if (jackbridge_is_ok() && index-- == 0)
{
static const char* ret[3] = { "Auto-Connect OFF", "Auto-Connect ON", nullptr };
static const char* ret[3] = { "Auto-Connect ON", "Auto-Connect OFF", nullptr };
return ret;
}

@@ -142,10 +142,9 @@ const EngineDriverDeviceInfo* CarlaEngine::getDriverDeviceInfo(const uint index2

if (jackbridge_is_ok() && index-- == 0)
{
static uint32_t bufSizes[11] = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 0 };
static EngineDriverDeviceInfo devInfo;
devInfo.hints = ENGINE_DRIVER_DEVICE_VARIABLE_BUFFER_SIZE;
devInfo.bufferSizes = bufSizes;
devInfo.bufferSizes = nullptr;
devInfo.sampleRates = nullptr;
return &devInfo;
}
@@ -285,7 +284,7 @@ void CarlaEngine::idle() noexcept
}
}

#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
pData->osc.idle();
#endif
}
@@ -303,7 +302,7 @@ bool CarlaEngine::addPlugin(const BinaryType btype, const PluginType ptype,
const void* const extra, const uint options)
{
CARLA_SAFE_ASSERT_RETURN_ERR(pData->isIdling == 0, "An operation is still being processed, please wait for it to finish");
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
CARLA_SAFE_ASSERT_RETURN_ERR(pData->plugins != nullptr, "Invalid engine internal data");
CARLA_SAFE_ASSERT_RETURN_ERR(pData->nextPluginId <= pData->maxPluginNumber, "Invalid engine internal data");
#endif
@@ -315,7 +314,7 @@ bool CarlaEngine::addPlugin(const BinaryType btype, const PluginType ptype,

uint id;

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
CarlaPlugin* oldPlugin = nullptr;

if (pData->nextPluginId < pData->curPluginCount)
@@ -338,7 +337,7 @@ bool CarlaEngine::addPlugin(const BinaryType btype, const PluginType ptype,
return false;
}

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
CARLA_SAFE_ASSERT_RETURN_ERR(pData->plugins[id].plugin == nullptr, "Invalid engine internal data");
#endif
}
@@ -486,7 +485,6 @@ bool CarlaEngine::addPlugin(const BinaryType btype, const PluginType ptype,

plugin->reload();

#ifndef BUILD_BRIDGE
bool canRun = true;

/**/ if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
@@ -517,11 +515,6 @@ bool CarlaEngine::addPlugin(const BinaryType btype, const PluginType ptype,
return false;
}

# ifdef HAVE_LIBLO
plugin->registerToOscClient();
# endif
#endif

EnginePluginData& pluginData(pData->plugins[id]);
pluginData.plugin = plugin;
pluginData.insPeak[0] = 0.0f;
@@ -529,7 +522,7 @@ bool CarlaEngine::addPlugin(const BinaryType btype, const PluginType ptype,
pluginData.outsPeak[0] = 0.0f;
pluginData.outsPeak[1] = 0.0f;

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (oldPlugin != nullptr)
{
CARLA_SAFE_ASSERT(! pData->loadingProject);
@@ -559,18 +552,24 @@ bool CarlaEngine::addPlugin(const BinaryType btype, const PluginType ptype,
else if (! pData->loadingProject)
#endif
{
plugin->setActive(true, true, false);
plugin->setEnabled(true);

++pData->curPluginCount;
callback(ENGINE_CALLBACK_PLUGIN_ADDED, id, 0, 0, 0.0f, plugin->getName());

#ifndef BUILD_BRIDGE
if (getType() != kEngineTypeBridge)
plugin->setActive(true, false, true);

#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
pData->graph.addPlugin(plugin);
#endif
}

#if defined(HAVE_LIBLO) && ! defined(BUILD_BRIDGE)
plugin->registerToOscClient();
#endif

return true;
}

@@ -582,7 +581,7 @@ bool CarlaEngine::addPlugin(const PluginType ptype, const char* const filename,
bool CarlaEngine::removePlugin(const uint id)
{
CARLA_SAFE_ASSERT_RETURN_ERR(pData->isIdling == 0, "An operation is still being processed, please wait for it to finish");
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
CARLA_SAFE_ASSERT_RETURN_ERR(pData->plugins != nullptr, "Invalid engine internal data");
CARLA_SAFE_ASSERT_RETURN_ERR(pData->curPluginCount != 0, "Invalid engine internal data");
#endif
@@ -597,7 +596,7 @@ bool CarlaEngine::removePlugin(const uint id)

const ScopedThreadStopper sts(this);

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
pData->graph.removePlugin(plugin);

@@ -612,7 +611,7 @@ bool CarlaEngine::removePlugin(const uint id)
}
*/

# ifdef HAVE_LIBLO
# if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
if (isOscControlRegistered())
oscSend_control_remove_plugin(id);
# endif
@@ -630,7 +629,7 @@ bool CarlaEngine::removePlugin(const uint id)
bool CarlaEngine::removeAllPlugins()
{
CARLA_SAFE_ASSERT_RETURN_ERR(pData->isIdling == 0, "An operation is still being processed, please wait for it to finish");
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
CARLA_SAFE_ASSERT_RETURN_ERR(pData->plugins != nullptr, "Invalid engine internal data");
CARLA_SAFE_ASSERT_RETURN_ERR(pData->nextPluginId == pData->maxPluginNumber, "Invalid engine internal data");
#endif
@@ -644,11 +643,11 @@ bool CarlaEngine::removeAllPlugins()

const uint curPluginCount(pData->curPluginCount);

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
pData->graph.removeAllPlugins();

# ifdef HAVE_LIBLO
# if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
if (isOscControlRegistered())
{
for (uint i=0; i < curPluginCount; ++i)
@@ -682,7 +681,7 @@ bool CarlaEngine::removeAllPlugins()
return true;
}

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
const char* CarlaEngine::renamePlugin(const uint id, const char* const newName)
{
CARLA_SAFE_ASSERT_RETURN_ERRN(pData->isIdling == 0, "An operation is still being processed, please wait for it to finish");
@@ -810,7 +809,7 @@ bool CarlaEngine::switchPlugins(const uint idA, const uint idB) noexcept

CarlaPlugin* CarlaEngine::getPlugin(const uint id) const noexcept
{
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
CARLA_SAFE_ASSERT_RETURN_ERRN(pData->plugins != nullptr, "Invalid engine internal data");
CARLA_SAFE_ASSERT_RETURN_ERRN(pData->curPluginCount != 0, "Invalid engine internal data");
#endif
@@ -1127,7 +1126,7 @@ const EngineOptions& CarlaEngine::getOptions() const noexcept
return pData->options;
}

const EngineTimeInfo& CarlaEngine::getTimeInfo() const noexcept
EngineTimeInfo CarlaEngine::getTimeInfo() const noexcept
{
return pData->timeInfo;
}
@@ -1171,11 +1170,11 @@ float CarlaEngine::getOutputPeak(const uint pluginId, const bool isLeft) const n
void CarlaEngine::callback(const EngineCallbackOpcode action, const uint pluginId, const int value1, const int value2, const float value3, const char* const valueStr) noexcept
{
#ifdef DEBUG
if (action != ENGINE_CALLBACK_IDLE)
if (action != ENGINE_CALLBACK_IDLE && action != ENGINE_CALLBACK_NOTE_ON && action != ENGINE_CALLBACK_NOTE_OFF)
carla_debug("CarlaEngine::callback(%i:%s, %i, %i, %i, %f, \"%s\")", action, EngineCallbackOpcode2Str(action), pluginId, value1, value2, value3, valueStr);
#endif

#ifdef BUILD_BRIDGE
#ifdef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (pData->isIdling)
#else
if (pData->isIdling && action != ENGINE_CALLBACK_PATCHBAY_CLIENT_DATA_CHANGED)
@@ -1301,6 +1300,13 @@ bool CarlaEngine::setAboutToClose() noexcept
return (pData->isIdling == 0);
}

#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
bool CarlaEngine::isLoadingProject() const noexcept
{
return pData->loadingProject;
}
#endif

// -----------------------------------------------------------------------
// Global options

@@ -1363,7 +1369,7 @@ void CarlaEngine::setOption(const EngineOption option, const int value, const ch
break;

case ENGINE_OPTION_PREFER_PLUGIN_BRIDGES:
#ifdef BUILD_BRIDGE
#ifdef BUILD_BRIDGE_ALTERNATIVE_ARCH
CARLA_SAFE_ASSERT_RETURN(value == 0,);
#else
CARLA_SAFE_ASSERT_RETURN(value == 0 || value == 1,);
@@ -1518,7 +1524,7 @@ void CarlaEngine::setOption(const EngineOption option, const int value, const ch
pData->options.frontendWinId = static_cast<uintptr_t>(winId);
} break;

#ifndef CARLA_OS_WIN
#if !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH) && !defined(CARLA_OS_WIN)
case ENGINE_OPTION_WINE_EXECUTABLE:
CARLA_SAFE_ASSERT_RETURN(valueStr != nullptr && valueStr[0] != '\0',);

@@ -1563,30 +1569,42 @@ void CarlaEngine::setOption(const EngineOption option, const int value, const ch
}
}

#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
#ifndef BUILD_BRIDGE
// -----------------------------------------------------------------------
// OSC Stuff

# ifndef BUILD_BRIDGE
bool CarlaEngine::isOscControlRegistered() const noexcept
{
# ifdef HAVE_LIBLO
return pData->osc.isControlRegistered();
}
# else
return false;
# endif
}

void CarlaEngine::idleOsc() const noexcept
{
# ifdef HAVE_LIBLO
pData->osc.idle();
# endif
}

const char* CarlaEngine::getOscServerPathTCP() const noexcept
{
# ifdef HAVE_LIBLO
return pData->osc.getServerPathTCP();
# else
return nullptr;
# endif
}

const char* CarlaEngine::getOscServerPathUDP() const noexcept
{
# ifdef HAVE_LIBLO
return pData->osc.getServerPathUDP();
# else
return nullptr;
# endif
}
#endif

@@ -1605,7 +1623,7 @@ void CarlaEngine::bufferSizeChanged(const uint32_t newBufferSize)
{
carla_debug("CarlaEngine::bufferSizeChanged(%i)", newBufferSize);

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK ||
pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
{
@@ -1630,7 +1648,7 @@ void CarlaEngine::sampleRateChanged(const double newSampleRate)
{
carla_debug("CarlaEngine::sampleRateChanged(%g)", newSampleRate);

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK ||
pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
{
@@ -1655,7 +1673,7 @@ void CarlaEngine::offlineModeChanged(const bool isOfflineNow)
{
carla_debug("CarlaEngine::offlineModeChanged(%s)", bool2str(isOfflineNow));

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK ||
pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
{
@@ -1691,7 +1709,7 @@ void CarlaEngine::saveProjectInternal(water::MemoryOutputStream& outStream) cons

if (plugin != nullptr && plugin->isEnabled())
{
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// deactivate bridge client-side ping check, since some plugins block during save
if (plugin->getHints() & PLUGIN_IS_BRIDGE)
plugin->setCustomData(CUSTOM_DATA_TYPE_STRING, "__CarlaPingOnOff__", "false", false);
@@ -1771,7 +1789,7 @@ void CarlaEngine::saveProjectInternal(water::MemoryOutputStream& outStream) cons
}
}

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// tell bridges we're done saving
for (uint i=0; i < pData->curPluginCount; ++i)
{
@@ -1935,7 +1953,7 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc)
return false;
}

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
const ScopedValueSetter<bool> _svs(pData->loadingProject, true, false);
#endif

@@ -2033,7 +2051,16 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc)
}
}

CARLA_SAFE_ASSERT_CONTINUE(option != -1);
if (option == -1)
{
// check old stuff, unhandled now
if (tag == "GIG_PATH")
continue;

// hmm something is wrong..
carla_stderr2("CarlaEngine::loadProjectInternal() - Unhandled option '%s'", tag.toRawUTF8());
continue;
}

setOption(static_cast<EngineOption>(option), value, valueStr);
}
@@ -2206,7 +2233,7 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc)
if (addPlugin(getBinaryTypeFromFile(stateSave.binary), ptype, stateSave.binary,
stateSave.name, stateSave.label, stateSave.uniqueId, extraStuff, stateSave.options))
{
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
const uint pluginId = pData->curPluginCount;
#else
const uint pluginId = 0;
@@ -2229,16 +2256,12 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc)
* When project is loading we do not enable the plugin right away,
* as we want to load state first.
*/
#ifdef BUILD_BRIDGE
plugin->setActive(true, true, false);
#else
++pData->curPluginCount;
#endif

plugin->setEnabled(true);

++pData->curPluginCount;
callback(ENGINE_CALLBACK_PLUGIN_ADDED, pluginId, 0, 0, 0.0f, plugin->getName());

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
pData->graph.addPlugin(plugin);
#endif
@@ -2258,7 +2281,7 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc)
return true;
}

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// tell bridges we're done loading
for (uint i=0; i < pData->curPluginCount; ++i)
{


+ 5
- 11
source/backend/engine/CarlaEngineBridge.cpp View File

@@ -190,7 +190,7 @@ public:
shmNonRtClientDataSize != sizeof(BridgeNonRtClientData) ||
shmNonRtServerDataSize != sizeof(BridgeNonRtServerData))
{
carla_stderr2("CarlaJackAppClient: data size mismatch");
carla_stderr2("CarlaEngineBridge: data size mismatch");
return false;
}

@@ -713,7 +713,7 @@ public:
const float value(fShmNonRtClientControl.readFloat());

if (plugin != nullptr && plugin->isEnabled())
plugin->setParameterValue(index, value, true, false, false);
plugin->setParameterValue(index, value, false, false, false);
break;
}

@@ -1190,6 +1190,8 @@ protected:
}

case kPluginBridgeRtClientProcess: {
const uint32_t frames(fShmRtClientControl.readUInt());

CARLA_SAFE_ASSERT_BREAK(fShmAudioPool.data != nullptr);

if (plugin != nullptr && plugin->isEnabled() && plugin->tryLock(fIsOffline))
@@ -1240,7 +1242,7 @@ protected:
}

plugin->initBuffers();
plugin->process(audioIn, audioOut, cvIn, cvOut, pData->bufferSize);
plugin->process(audioIn, audioOut, cvIn, cvOut, frames);
plugin->unlock();
}

@@ -1409,14 +1411,6 @@ CARLA_BACKEND_END_NAMESPACE

// -----------------------------------------------------------------------

#if defined(CARLA_OS_WIN) && ! defined(__WINE__)
extern "C" __declspec (dllexport)
#else
extern "C" __attribute__ ((visibility("default")))
#endif
void carla_register_native_plugin_carla();
void carla_register_native_plugin_carla(){}

#include "CarlaBridgeUtils.cpp"

// -----------------------------------------------------------------------

+ 35
- 0
source/backend/engine/CarlaEngineData.cpp View File

@@ -304,6 +304,17 @@ EngineTimeInfoBBT::EngineTimeInfoBBT() noexcept
ticksPerBeat(0.0),
beatsPerMinute(0.0) {}

EngineTimeInfoBBT::EngineTimeInfoBBT(const EngineTimeInfoBBT& bbt) noexcept
: valid(bbt.valid),
bar(bbt.bar),
beat(bbt.beat),
tick(bbt.tick),
barStartTick(bbt.barStartTick),
beatsPerBar(bbt.beatsPerBar),
beatType(bbt.beatType),
ticksPerBeat(bbt.ticksPerBeat),
beatsPerMinute(bbt.beatsPerMinute) {}

// -----------------------------------------------------------------------
// EngineTimeInfo

@@ -321,6 +332,30 @@ void EngineTimeInfo::clear() noexcept
carla_zeroStruct(bbt);
}

EngineTimeInfo::EngineTimeInfo(const EngineTimeInfo& info) noexcept
: playing(info.playing),
frame(info.frame),
usecs(info.usecs),
bbt(info.bbt) {}

EngineTimeInfo& EngineTimeInfo::operator=(const EngineTimeInfo& info) noexcept
{
playing = info.playing;
frame = info.frame;
usecs = info.usecs;
bbt.valid = info.bbt.valid;
bbt.bar = info.bbt.bar;
bbt.tick = info.bbt.tick;
bbt.tick = info.bbt.tick;
bbt.barStartTick = info.bbt.barStartTick;
bbt.beatsPerBar = info.bbt.beatsPerBar;
bbt.beatType = info.bbt.beatType;
bbt.ticksPerBeat = info.bbt.ticksPerBeat;
bbt.beatsPerMinute = info.bbt.beatsPerMinute;

return *this;
}

bool EngineTimeInfo::operator==(const EngineTimeInfo& timeInfo) const noexcept
{
if (timeInfo.playing != playing || timeInfo.frame != frame || timeInfo.bbt.valid != bbt.valid)


+ 31
- 87
source/backend/engine/CarlaEngineInternal.cpp View File

@@ -176,10 +176,11 @@ void EngineInternalTime::fillEngineTimeInfo(const uint32_t newFrames) noexcept

double ticktmp;

timeInfo.usecs = 0;

if (transportMode == ENGINE_TRANSPORT_MODE_INTERNAL)
{
timeInfo.usecs = 0;
timeInfo.frame = nextFrame;
}

if (needsReset)
{
@@ -256,74 +257,19 @@ void EngineInternalTime::fillJackTimeInfo(jack_position_t* const pos, const uint
{
CARLA_SAFE_ASSERT_RETURN(carla_isNotZero(sampleRate),);
CARLA_SAFE_ASSERT_RETURN(newFrames > 0,);

double ticktmp;

if (needsReset)
{
pos->valid = JackPositionBBT;
pos->beat_type = 4.0f;
pos->ticks_per_beat = kTicksPerBeat;

double abs_beat, abs_tick;

#if defined(HAVE_HYLIA) && !defined(BUILD_BRIDGE)
if (hylia.enabled)
{
if (hylia.timeInfo.beat >= 0.0)
{
abs_beat = hylia.timeInfo.beat;
abs_tick = abs_beat * kTicksPerBeat;
}
else
{
abs_beat = 0.0;
abs_tick = 0.0;
timeInfo.playing = false;
}
}
else
#endif
{
const double min = static_cast<double>(pos->frame) / (sampleRate * 60.0);
abs_beat = min * beatsPerMinute;
abs_tick = abs_beat * kTicksPerBeat;
}

const double bar = std::floor(abs_beat / beatsPerBar);
const double beat = std::floor(std::fmod(abs_beat, beatsPerBar));

pos->bar = static_cast<int32_t>(bar) + 1;
pos->beat = static_cast<int32_t>(beat) + 1;
pos->bar_start_tick = ((bar * beatsPerBar) + beat) * kTicksPerBeat;

ticktmp = abs_tick - pos->bar_start_tick;
}
else if (timeInfo.playing)
{
ticktmp = tick + (newFrames * kTicksPerBeat * beatsPerMinute / (sampleRate * 60.0));

while (ticktmp >= kTicksPerBeat)
{
ticktmp -= kTicksPerBeat;

if (++pos->beat > beatsPerBar)
{
++pos->bar;
pos->beat = 1;
pos->bar_start_tick += beatsPerBar * kTicksPerBeat;
}
}
}
else
{
ticktmp = tick;
}

pos->beats_per_bar = static_cast<float>(beatsPerBar);
CARLA_SAFE_ASSERT(transportMode == ENGINE_TRANSPORT_MODE_JACK);

fillEngineTimeInfo(newFrames);

pos->valid = JackPositionBBT;
pos->bar = timeInfo.bbt.bar;
pos->beat = timeInfo.bbt.beat;
pos->tick = static_cast<int32_t>(tick + 0.5);
pos->bar_start_tick = timeInfo.bbt.barStartTick;
pos->beats_per_bar = timeInfo.bbt.beatsPerBar;
pos->beat_type = timeInfo.bbt.beatType;
pos->ticks_per_beat = kTicksPerBeat;
pos->beats_per_minute = beatsPerMinute;
pos->tick = ticktmp;
tick = ticktmp;
}

void EngineInternalTime::preProcess(const uint32_t numFrames)
@@ -418,7 +364,7 @@ void EngineNextAction::clearAndReset() noexcept

CarlaEngine::ProtectedData::ProtectedData(CarlaEngine* const engine) noexcept
: thread(engine),
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
osc(engine),
oscData(nullptr),
#endif
@@ -426,7 +372,7 @@ CarlaEngine::ProtectedData::ProtectedData(CarlaEngine* const engine) noexcept
callbackPtr(nullptr),
fileCallback(nullptr),
fileCallbackPtr(nullptr),
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
loadingProject(false),
#endif
hints(0x0),
@@ -442,17 +388,17 @@ CarlaEngine::ProtectedData::ProtectedData(CarlaEngine* const engine) noexcept
name(),
options(),
timeInfo(),
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
plugins(nullptr),
#endif
events(),
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
graph(engine),
#endif
time(timeInfo, options.transportMode),
nextAction()
{
#ifdef BUILD_BRIDGE
#ifdef BUILD_BRIDGE_ALTERNATIVE_ARCH
carla_zeroStructs(plugins, 1);
#endif
}
@@ -463,7 +409,7 @@ CarlaEngine::ProtectedData::~ProtectedData() noexcept
CARLA_SAFE_ASSERT(maxPluginNumber == 0);
CARLA_SAFE_ASSERT(nextPluginId == 0);
CARLA_SAFE_ASSERT(isIdling == 0);
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
CARLA_SAFE_ASSERT(plugins == nullptr);
#endif
}
@@ -473,13 +419,13 @@ CarlaEngine::ProtectedData::~ProtectedData() noexcept
bool CarlaEngine::ProtectedData::init(const char* const clientName)
{
CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(name.isEmpty(), "Invalid engine internal data (err #1)");
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(oscData == nullptr, "Invalid engine internal data (err #2)");
#endif
CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(events.in == nullptr, "Invalid engine internal data (err #4)");
CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(events.out == nullptr, "Invalid engine internal data (err #5)");
CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(clientName != nullptr && clientName[0] != '\0', "Invalid client name");
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
CARLA_SAFE_ASSERT_RETURN_INTERNAL_ERR(plugins == nullptr, "Invalid engine internal data (err #3)");
#endif

@@ -525,14 +471,12 @@ bool CarlaEngine::ProtectedData::init(const char* const clientName)

timeInfo.clear();

#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
osc.init(clientName);
# ifndef BUILD_BRIDGE
oscData = osc.getControlData();
# endif
#endif

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
plugins = new EnginePluginData[maxPluginNumber];
carla_zeroStructs(plugins, maxPluginNumber);
#endif
@@ -546,7 +490,7 @@ bool CarlaEngine::ProtectedData::init(const char* const clientName)
void CarlaEngine::ProtectedData::close()
{
CARLA_SAFE_ASSERT(name.isNotEmpty());
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
CARLA_SAFE_ASSERT(plugins != nullptr);
CARLA_SAFE_ASSERT(nextPluginId == maxPluginNumber);
#endif
@@ -556,7 +500,7 @@ void CarlaEngine::ProtectedData::close()
thread.stopThread(500);
nextAction.clearAndReset();

#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
osc.close();
oscData = nullptr;
#endif
@@ -566,7 +510,7 @@ void CarlaEngine::ProtectedData::close()
maxPluginNumber = 0;
nextPluginId = 0;

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (plugins != nullptr)
{
delete[] plugins;
@@ -595,7 +539,7 @@ void CarlaEngine::ProtectedData::initTime(const char* const features)

// -----------------------------------------------------------------------

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
void CarlaEngine::ProtectedData::doPluginRemove(const uint pluginId) noexcept
{
CARLA_SAFE_ASSERT_RETURN(curPluginCount > 0,);
@@ -656,7 +600,7 @@ void CarlaEngine::ProtectedData::doNextPluginAction() noexcept

const EnginePostAction opcode = nextAction.opcode;
const bool needsPost = nextAction.needsPost;
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
const uint pluginId = nextAction.pluginId;
const uint value = nextAction.value;
#endif
@@ -675,7 +619,7 @@ void CarlaEngine::ProtectedData::doNextPluginAction() noexcept
case kEnginePostActionZeroCount:
curPluginCount = 0;
break;
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
case kEnginePostActionRemovePlugin:
doPluginRemove(pluginId);
break;


+ 8
- 12
source/backend/engine/CarlaEngineInternal.hpp View File

@@ -21,7 +21,7 @@
#include "CarlaEngineThread.hpp"
#include "CarlaEngineUtils.hpp"

#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
#ifndef BUILD_BRIDGE
# include "CarlaEngineOsc.hpp"
# include "hylia/hylia.h"
#endif
@@ -57,7 +57,7 @@ struct EngineInternalEvents {
CARLA_DECLARE_NON_COPY_STRUCT(EngineInternalEvents)
};

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// -----------------------------------------------------------------------
// InternalGraph

@@ -111,7 +111,7 @@ private:
CARLA_PREVENT_HEAP_ALLOCATION
CARLA_DECLARE_NON_COPY_STRUCT(EngineInternalGraph)
};
#endif
#endif // BUILD_BRIDGE_ALTERNATIVE_ARCH

// -----------------------------------------------------------------------
// InternalTime
@@ -170,7 +170,7 @@ private:
enum EnginePostAction {
kEnginePostActionNull = 0,
kEnginePostActionZeroCount, // set curPluginCount to 0
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
kEnginePostActionRemovePlugin, // remove a plugin
kEnginePostActionSwitchPlugins // switch between 2 plugins
#endif
@@ -209,13 +209,9 @@ struct EnginePluginData {
struct CarlaEngine::ProtectedData {
CarlaEngineThread thread;

#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
CarlaEngineOsc osc;
# ifdef BUILD_BRIDGE
CarlaOscData* oscData;
# else
const CarlaOscData* oscData;
# endif
#endif

EngineCallbackFunc callback;
@@ -224,7 +220,7 @@ struct CarlaEngine::ProtectedData {
FileCallbackFunc fileCallback;
void* fileCallbackPtr;

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
bool loadingProject;
#endif

@@ -244,14 +240,14 @@ struct CarlaEngine::ProtectedData {
EngineOptions options;
EngineTimeInfo timeInfo;

#ifdef BUILD_BRIDGE
#ifdef BUILD_BRIDGE_ALTERNATIVE_ARCH
EnginePluginData plugins[1];
#else
EnginePluginData* plugins;
#endif

EngineInternalEvents events;
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
EngineInternalGraph graph;
#endif
EngineInternalTime time;


+ 122
- 57
source/backend/engine/CarlaEngineJack.cpp View File

@@ -811,6 +811,7 @@ public:
fIsRunning(false)
#else
fTimebaseMaster(false),
fTimebaseRolling(false),
fUsedGroups(),
fUsedPorts(),
fUsedConnections(),
@@ -950,8 +951,12 @@ public:
jackbridge_set_process_callback(fClient, carla_jack_process_callback, this);
jackbridge_on_shutdown(fClient, carla_jack_shutdown_callback, this);

fTimebaseRolling = false;

if (opts.transportMode == ENGINE_TRANSPORT_MODE_JACK)
fTimebaseMaster = jackbridge_set_timebase_callback(fClient, true, carla_jack_timebase_callback, this);
else
fTimebaseMaster = false;

if (opts.processMode != ENGINE_PROCESS_MODE_PATCHBAY)
initJackPatchbay(jackClientName);
@@ -984,6 +989,45 @@ public:

if (jackbridge_activate(fClient))
{
if (opts.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK ||
opts.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
{
if (pData->options.audioDevice != nullptr &&
std::strcmp(pData->options.audioDevice, "Auto-Connect ON") == 0 &&
std::getenv("LADISH_APP_NAME") == nullptr &&
std::getenv("NSM_URL") == nullptr)
{
char strBuf[STR_MAX];
strBuf[STR_MAX-1] = '\0';

if (jackbridge_port_by_name(fClient, "system:capture_1") != nullptr)
{
std::snprintf(strBuf, STR_MAX-2, "%s:audio-in1", jackClientName);
jackbridge_connect(fClient, "system:capture_1", strBuf);

std::snprintf(strBuf, STR_MAX-2, "%s:audio-in2", jackClientName);

if (jackbridge_port_by_name(fClient, "system:capture_2") != nullptr)
jackbridge_connect(fClient, "system:capture_2", strBuf);
else
jackbridge_connect(fClient, "system:capture_1", strBuf);
}

if (jackbridge_port_by_name(fClient, "system:playback_1") != nullptr)
{
std::snprintf(strBuf, STR_MAX-2, "%s:audio-out1", jackClientName);
jackbridge_connect(fClient, strBuf, "system:playback_1");

std::snprintf(strBuf, STR_MAX-2, "%s:audio-out2", jackClientName);

if (jackbridge_port_by_name(fClient, "system:playback_2") != nullptr)
jackbridge_connect(fClient, strBuf, "system:playback_2");
else
jackbridge_connect(fClient, strBuf, "system:playback_1");
}
}
}

startThread();
callback(ENGINE_CALLBACK_ENGINE_STARTED, 0,
opts.processMode, opts.transportMode,
@@ -992,7 +1036,8 @@ public:
return true;
}

if (opts.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK || opts.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
if (opts.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK ||
opts.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
{
pData->graph.destroy();
}
@@ -1084,6 +1129,55 @@ public:
return "JACK";
}

EngineTimeInfo getTimeInfo() const noexcept override
{
if (pData->options.transportMode != ENGINE_TRANSPORT_MODE_JACK)
return CarlaEngine::getTimeInfo();
if (pData->options.processMode != ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS)
return CarlaEngine::getTimeInfo();

jack_position_t jpos;

// invalidate
jpos.unique_1 = 1;
jpos.unique_2 = 2;

EngineTimeInfo timeInfo;
const bool playing = jackbridge_transport_query(fClient, &jpos) == JackTransportRolling;

if (jpos.unique_1 != jpos.unique_2)
{
timeInfo.playing = false;
timeInfo.frame = 0;
timeInfo.usecs = 0;
timeInfo.bbt.valid = false;
return timeInfo;
}

timeInfo.playing = playing;
timeInfo.frame = jpos.frame;
timeInfo.usecs = jpos.usecs;

if (jpos.valid & JackPositionBBT)
{
timeInfo.bbt.valid = true;
timeInfo.bbt.bar = jpos.bar;
timeInfo.bbt.beat = jpos.beat;
timeInfo.bbt.tick = jpos.tick;
timeInfo.bbt.barStartTick = jpos.bar_start_tick;
timeInfo.bbt.beatsPerBar = jpos.beats_per_bar;
timeInfo.bbt.beatType = jpos.beat_type;
timeInfo.bbt.ticksPerBeat = jpos.ticks_per_beat;
timeInfo.bbt.beatsPerMinute = jpos.beats_per_minute;
}
else
{
timeInfo.bbt.valid = false;
}

return timeInfo;
}

#ifndef BUILD_BRIDGE
void setOption(const EngineOption option, const int value, const char* const valueStr) noexcept override
{
@@ -1390,7 +1484,7 @@ public:

void transportPlay() noexcept override
{
if (pData->options.transportMode == ENGINE_TRANSPORT_MODE_INTERNAL)
if (pData->options.transportMode != ENGINE_TRANSPORT_MODE_JACK)
return CarlaEngine::transportPlay();

if (fClient != nullptr)
@@ -1410,7 +1504,7 @@ public:

void transportPause() noexcept override
{
if (pData->options.transportMode == ENGINE_TRANSPORT_MODE_INTERNAL)
if (pData->options.transportMode != ENGINE_TRANSPORT_MODE_JACK)
return CarlaEngine::transportPause();

if (fClient != nullptr)
@@ -1423,9 +1517,10 @@ public:

void transportBPM(const double bpm) noexcept override
{
CarlaEngine::transportBPM(bpm);
if (pData->options.transportMode != ENGINE_TRANSPORT_MODE_JACK || fTimebaseMaster)
return CarlaEngine::transportBPM(bpm);

if (fClient == nullptr || fTimebaseMaster)
if (fClient == nullptr)
return;

jack_position_t jpos;
@@ -1448,7 +1543,7 @@ public:

void transportRelocate(const uint64_t frame) noexcept override
{
if (pData->options.transportMode == ENGINE_TRANSPORT_MODE_INTERNAL)
if (pData->options.transportMode != ENGINE_TRANSPORT_MODE_JACK)
return CarlaEngine::transportRelocate(frame);

if (fClient != nullptr)
@@ -1555,57 +1650,12 @@ protected:
offlineModeChanged(isFreewheel);
}

void saveTransportInfo()
{
if (pData->options.transportMode != ENGINE_TRANSPORT_MODE_JACK)
return;

jack_position_t jpos;

// invalidate
jpos.unique_1 = 1;
jpos.unique_2 = 2;

pData->timeInfo.playing = (jackbridge_transport_query(fClient, &jpos) == JackTransportRolling);

if (jpos.unique_1 == jpos.unique_2)
{
pData->timeInfo.frame = jpos.frame;
pData->timeInfo.usecs = jpos.usecs;

if (jpos.valid & JackPositionBBT)
{
pData->timeInfo.bbt.valid = true;
pData->timeInfo.bbt.bar = jpos.bar;
pData->timeInfo.bbt.beat = jpos.beat;
pData->timeInfo.bbt.tick = jpos.tick;
pData->timeInfo.bbt.barStartTick = jpos.bar_start_tick;
pData->timeInfo.bbt.beatsPerBar = jpos.beats_per_bar;
pData->timeInfo.bbt.beatType = jpos.beat_type;
pData->timeInfo.bbt.ticksPerBeat = jpos.ticks_per_beat;
pData->timeInfo.bbt.beatsPerMinute = jpos.beats_per_minute;
}
else
{
pData->timeInfo.bbt.valid = false;
}
}
else
{
pData->timeInfo.frame = 0;
pData->timeInfo.usecs = 0;
pData->timeInfo.bbt.valid = false;
}
}

void handleJackProcessCallback(const uint32_t nframes)
{
const PendingRtEventsRunner prt(this, nframes);

CARLA_SAFE_ASSERT_RETURN(nframes == pData->bufferSize,);

saveTransportInfo();

#ifdef BUILD_BRIDGE
CarlaPlugin* const plugin(pData->plugins[0].plugin);

@@ -1781,6 +1831,17 @@ protected:
}
}
}

if (fTimebaseMaster)
{
const bool playing = jackbridge_transport_query(fClient, nullptr) == JackTransportRolling;

if (fTimebaseRolling != playing)
{
fTimebaseRolling = playing;
pData->timeInfo.playing = playing;
}
}
#endif // ! BUILD_BRIDGE
}

@@ -1795,6 +1856,9 @@ protected:
if (new_pos)
pData->time.setNeedsReset();

pData->timeInfo.playing = fTimebaseRolling;
pData->timeInfo.frame = pos->frame;
pData->timeInfo.usecs = pos->usecs;
pData->time.fillJackTimeInfo(pos, nframes);
}

@@ -2053,6 +2117,8 @@ private:
jack_port_t* fRackPorts[kRackPortCount];

bool fTimebaseMaster;
bool fTimebaseRolling;

PatchbayGroupList fUsedGroups;
PatchbayPortList fUsedPorts;
PatchbayConnectionList fUsedConnections;
@@ -2484,15 +2550,15 @@ private:
handlePtr->handleJackFreewheelCallback(bool(starting));
}

static int JACKBRIDGE_API carla_jack_process_callback(jack_nframes_t nframes, void* arg) __attribute__((annotate("realtime")))
static void JACKBRIDGE_API carla_jack_latency_callback(jack_latency_callback_mode_t mode, void* arg)
{
handlePtr->handleJackProcessCallback(nframes);
return 0;
handlePtr->handleJackLatencyCallback(mode);
}

static void JACKBRIDGE_API carla_jack_latency_callback(jack_latency_callback_mode_t mode, void* arg)
static int JACKBRIDGE_API carla_jack_process_callback(jack_nframes_t nframes, void* arg) __attribute__((annotate("realtime")))
{
handlePtr->handleJackLatencyCallback(mode);
handlePtr->handleJackProcessCallback(nframes);
return 0;
}

#ifndef BUILD_BRIDGE
@@ -2565,7 +2631,6 @@ private:
if (plugin->tryLock(engine->fFreewheel))
{
plugin->initBuffers();
engine->saveTransportInfo();
engine->processPlugin(plugin, nframes);
plugin->unlock();
}


+ 4
- 3
source/backend/engine/CarlaEngineNative.cpp View File

@@ -17,8 +17,8 @@

#include "CarlaDefines.h"

#ifdef BUILD_BRIDGE
# error This file should not be compiled if building bridge
#ifdef BUILD_BRIDGE_ALTERNATIVE_ARCH
# error This file should not be compiled if building alternative-arch bridges
#endif

#include "CarlaEngineInternal.hpp"
@@ -1165,7 +1165,7 @@ protected:

const CarlaMutexLocker cml(fUiServer.getPipeLock());

#ifdef HAVE_LIBLO
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
if (! fUiServer.writeAndFixMessage("osc-urls"))
return;
if (! fUiServer.writeAndFixMessage(pData->osc.getServerPathTCP()))
@@ -2348,6 +2348,7 @@ CARLA_BACKEND_END_NAMESPACE
#include "CarlaHostCommon.cpp"
#include "CarlaPluginUI.cpp"
#include "CarlaDssiUtils.cpp"
#include "CarlaMacUtils.cpp"
#include "CarlaPatchbayUtils.cpp"
#include "CarlaPipeUtils.cpp"
#include "CarlaStateUtils.cpp"


+ 33
- 18
source/backend/engine/CarlaEngineRtAudio.cpp View File

@@ -1193,7 +1193,34 @@ const EngineDriverDeviceInfo* CarlaEngine::getRtAudioDeviceInfo(const uint index
if (index >= gRtAudioApis.size())
return nullptr;

static EngineDriverDeviceInfo devInfo = { 0x0, nullptr, nullptr };
static uint32_t dummyBufferSizes[] = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 0 };
static double dummySampleRates[] = { 22050.0, 32000.0, 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0, 0.0 };

// reset
devInfo.hints = 0x0;

// cleanup
if (devInfo.bufferSizes != nullptr && devInfo.bufferSizes != dummyBufferSizes)
{
delete[] devInfo.bufferSizes;
devInfo.bufferSizes = nullptr;
}
if (devInfo.sampleRates != nullptr && devInfo.sampleRates != dummySampleRates)
{
delete[] devInfo.sampleRates;
devInfo.sampleRates = nullptr;
}

const RtAudio::Api& api(gRtAudioApis[index]);

if (api == RtAudio::UNIX_JACK)
{
devInfo.bufferSizes = nullptr;
devInfo.sampleRates = nullptr;
return &devInfo;
}

RtAudio::DeviceInfo rtAudioDevInfo;

try {
@@ -1214,25 +1241,10 @@ const EngineDriverDeviceInfo* CarlaEngine::getRtAudioDeviceInfo(const uint index
}

if (i == devCount)
return nullptr;
rtAudioDevInfo = rtAudio.getDeviceInfo(rtAudio.getDefaultOutputDevice());

} CARLA_SAFE_EXCEPTION_RETURN("RtAudio device discovery", nullptr);

static EngineDriverDeviceInfo devInfo = { 0x0, nullptr, nullptr };
static uint32_t dummyBufferSizes[] = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 0 };
static double dummySampleRates[] = { 22050.0, 32000.0, 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0, 0.0 };

// reset
devInfo.hints = 0x0;
devInfo.bufferSizes = dummyBufferSizes;

// cleanup
if (devInfo.sampleRates != nullptr && devInfo.sampleRates != dummySampleRates)
{
delete[] devInfo.sampleRates;
devInfo.sampleRates = nullptr;
}

// a few APIs can do triple buffer
switch (api)
{
@@ -1245,13 +1257,16 @@ const EngineDriverDeviceInfo* CarlaEngine::getRtAudioDeviceInfo(const uint index
break;
}

// always use default buffer sizes
devInfo.bufferSizes = dummyBufferSizes;

// valid sample rates
if (const size_t sampleRatesCount = rtAudioDevInfo.sampleRates.size())
{
double* const sampleRates(new double[sampleRatesCount+1]);

for (size_t j=0; j < sampleRatesCount; ++j)
sampleRates[j] = rtAudioDevInfo.sampleRates[j];
for (size_t i=0; i < sampleRatesCount; ++i)
sampleRates[i] = rtAudioDevInfo.sampleRates[i];
sampleRates[sampleRatesCount] = 0.0;

devInfo.sampleRates = sampleRates;


+ 8
- 12
source/backend/engine/CarlaEngineThread.cpp View File

@@ -41,21 +41,17 @@ CarlaEngineThread::~CarlaEngineThread() noexcept
void CarlaEngineThread::run() noexcept
{
CARLA_SAFE_ASSERT_RETURN(kEngine != nullptr,);
#ifndef BUILD_BRIDGE
CARLA_SAFE_ASSERT(kEngine->isRunning());
#endif
carla_debug("CarlaEngineThread::run()");

#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
const bool isPlugin(kEngine->getType() == kEngineTypePlugin);
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
const bool kIsPlugin(kEngine->getType() == kEngineTypePlugin);
#endif
float value;

#ifdef BUILD_BRIDGE
for (; ! shouldThreadExit();)
#else
for (; kEngine->isRunning() && ! shouldThreadExit();)
#endif
// thread must do something...
CARLA_SAFE_ASSERT_RETURN(kEngine->getType() == kEngineTypeBridge || kEngine->isRunning(),);

for (; (kEngine->getType() == kEngineTypeBridge || kEngine->isRunning()) && ! shouldThreadExit();)
{
#if defined(HAVE_LIBLO) && ! defined(BUILD_BRIDGE)
const bool oscRegisted = kEngine->isOscControlRegistered();
@@ -63,8 +59,8 @@ void CarlaEngineThread::run() noexcept
const bool oscRegisted = false;
#endif

#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
if (isPlugin)
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
if (kIsPlugin)
kEngine->idleOsc();
#endif



+ 6
- 2
source/backend/engine/Makefile View File

@@ -15,11 +15,15 @@ OBJS = \
$(OBJDIR)/CarlaEngineData.cpp.o \
$(OBJDIR)/CarlaEngineGraph.cpp.o \
$(OBJDIR)/CarlaEngineInternal.cpp.o \
$(OBJDIR)/CarlaEngineOsc.cpp.o \
$(OBJDIR)/CarlaEngineOscSend.cpp.o \
$(OBJDIR)/CarlaEnginePorts.cpp.o \
$(OBJDIR)/CarlaEngineThread.cpp.o

ifeq ($(HAVE_LIBLO),true)
OBJS += \
$(OBJDIR)/CarlaEngineOsc.cpp.o \
$(OBJDIR)/CarlaEngineOscSend.cpp.o
endif

OBJSa = $(OBJS) \
$(OBJDIR)/CarlaEngineJack.cpp.o \
$(OBJDIR)/CarlaEngineNative.cpp.o \


+ 88
- 79
source/backend/plugin/CarlaPlugin.cpp View File

@@ -356,7 +356,7 @@ void CarlaPlugin::getParameterScalePointLabel(const uint32_t parameterId, const

float CarlaPlugin::getInternalParameterValue(const int32_t parameterId) const noexcept
{
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
CARLA_SAFE_ASSERT_RETURN(parameterId != PARAMETER_NULL && parameterId > PARAMETER_MAX, 0.0f);

switch (parameterId)
@@ -498,14 +498,14 @@ const CarlaStateSave& CarlaPlugin::getStateSave(const bool callPrepareForSave)
pData->stateSave.name = carla_strdup(pData->name);
pData->stateSave.label = carla_strdup(strBuf);
pData->stateSave.uniqueId = getUniqueId();
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
pData->stateSave.options = pData->options;
#endif

if (pData->filename != nullptr)
pData->stateSave.binary = carla_strdup(pData->filename);

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// ---------------------------------------------------------------
// Internals

@@ -578,7 +578,7 @@ const CarlaStateSave& CarlaPlugin::getStateSave(const bool callPrepareForSave)

stateParameter->dummy = dummy;
stateParameter->index = paramData.index;
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
stateParameter->midiCC = paramData.midiCC;
stateParameter->midiChannel = paramData.midiChannel;
#endif
@@ -794,7 +794,7 @@ void CarlaPlugin::loadStateSave(const CarlaStateSave& stateSave)
setParameterValue(static_cast<uint32_t>(index), stateParameter->value, true, true, true);
}

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
setParameterMidiCC(static_cast<uint32_t>(index), stateParameter->midiCC, true, true);
setParameterMidiChannel(static_cast<uint32_t>(index), stateParameter->midiChannel, true, true);
#endif
@@ -867,7 +867,7 @@ void CarlaPlugin::loadStateSave(const CarlaStateSave& stateSave)
#endif
}

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// ---------------------------------------------------------------
// Part 6 - set internal stuff

@@ -888,9 +888,10 @@ void CarlaPlugin::loadStateSave(const CarlaStateSave& stateSave)
setPanning(stateSave.panning, true, true);
setCtrlChannel(stateSave.ctrlChannel, true, true);
setActive(stateSave.active, true, true);
#endif

pData->engine->callback(ENGINE_CALLBACK_UPDATE, pData->id, 0, 0, 0.0f, nullptr);
if (! pData->engine->isLoadingProject())
pData->engine->callback(ENGINE_CALLBACK_UPDATE, pData->id, 0, 0, 0.0f, nullptr);
#endif
}

bool CarlaPlugin::saveStateToFile(const char* const filename)
@@ -1270,7 +1271,7 @@ void CarlaPlugin::setOption(const uint option, const bool yesNo, const bool send
else
pData->options &= ~option;

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (sendCallback)
pData->engine->callback(ENGINE_CALLBACK_OPTION_CHANGED, pData->id, static_cast<int>(option), yesNo ? 1 : 0, 0.0f, nullptr);
#else
@@ -1295,9 +1296,11 @@ void CarlaPlugin::setEnabled(const bool yesNo) noexcept

void CarlaPlugin::setActive(const bool active, const bool sendOsc, const bool sendCallback) noexcept
{
#ifndef BUILD_BRIDGE
CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
#endif
if (pData->bridged) {
CARLA_SAFE_ASSERT_RETURN(!sendOsc && !sendCallback,);
} else {
CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
}

if (pData->active == active)
return;
@@ -1313,25 +1316,27 @@ void CarlaPlugin::setActive(const bool active, const bool sendOsc, const bool se

pData->active = active;

#ifndef BUILD_BRIDGE
const float value(active ? 1.0f : 0.0f);
const float value = active ? 1.0f : 0.0f;

# ifdef HAVE_LIBLO
#if defined(HAVE_LIBLO) && ! defined(BUILD_BRIDGE)
if (sendOsc && pData->engine->isOscControlRegistered())
pData->engine->oscSend_control_set_parameter_value(pData->id, PARAMETER_ACTIVE, value);
# endif
#endif

#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (sendCallback)
pData->engine->callback(ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED, pData->id, PARAMETER_ACTIVE, 0, value, nullptr);
#endif

// may be unused
return; (void)sendOsc; (void)sendCallback;
}

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
void CarlaPlugin::setDryWet(const float value, const bool sendOsc, const bool sendCallback) noexcept
{
if (pData->bridged) {
CARLA_SAFE_ASSERT_RETURN(!sendOsc && !sendCallback,);
} else {
CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
}
CARLA_SAFE_ASSERT(value >= 0.0f && value <= 1.0f);

const float fixedValue(carla_fixedValue<float>(0.0f, 1.0f, value));
@@ -1341,20 +1346,22 @@ void CarlaPlugin::setDryWet(const float value, const bool sendOsc, const bool se

pData->postProc.dryWet = fixedValue;

#ifdef HAVE_LIBLO
# if defined(HAVE_LIBLO) && ! defined(BUILD_BRIDGE)
if (sendOsc && pData->engine->isOscControlRegistered())
pData->engine->oscSend_control_set_parameter_value(pData->id, PARAMETER_DRYWET, fixedValue);
#endif
# endif

if (sendCallback)
pData->engine->callback(ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED, pData->id, PARAMETER_DRYWET, 0, fixedValue, nullptr);

// may be unused
return; (void)sendOsc;
}

void CarlaPlugin::setVolume(const float value, const bool sendOsc, const bool sendCallback) noexcept
{
if (pData->bridged) {
CARLA_SAFE_ASSERT_RETURN(!sendOsc && !sendCallback,);
} else {
CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
}
CARLA_SAFE_ASSERT(value >= 0.0f && value <= 1.27f);

const float fixedValue(carla_fixedValue<float>(0.0f, 1.27f, value));
@@ -1364,20 +1371,22 @@ void CarlaPlugin::setVolume(const float value, const bool sendOsc, const bool se

pData->postProc.volume = fixedValue;

#ifdef HAVE_LIBLO
# if defined(HAVE_LIBLO) && ! defined(BUILD_BRIDGE)
if (sendOsc && pData->engine->isOscControlRegistered())
pData->engine->oscSend_control_set_parameter_value(pData->id, PARAMETER_VOLUME, fixedValue);
#endif
# endif

if (sendCallback)
pData->engine->callback(ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED, pData->id, PARAMETER_VOLUME, 0, fixedValue, nullptr);

// may be unused
return; (void)sendOsc;
}

void CarlaPlugin::setBalanceLeft(const float value, const bool sendOsc, const bool sendCallback) noexcept
{
if (pData->bridged) {
CARLA_SAFE_ASSERT_RETURN(!sendOsc && !sendCallback,);
} else {
CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
}
CARLA_SAFE_ASSERT(value >= -1.0f && value <= 1.0f);

const float fixedValue(carla_fixedValue<float>(-1.0f, 1.0f, value));
@@ -1387,20 +1396,22 @@ void CarlaPlugin::setBalanceLeft(const float value, const bool sendOsc, const bo

pData->postProc.balanceLeft = fixedValue;

#ifdef HAVE_LIBLO
# if defined(HAVE_LIBLO) && ! defined(BUILD_BRIDGE)
if (sendOsc && pData->engine->isOscControlRegistered())
pData->engine->oscSend_control_set_parameter_value(pData->id, PARAMETER_BALANCE_LEFT, fixedValue);
#endif
# endif

if (sendCallback)
pData->engine->callback(ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED, pData->id, PARAMETER_BALANCE_LEFT, 0, fixedValue, nullptr);

// may be unused
return; (void)sendOsc;
}

void CarlaPlugin::setBalanceRight(const float value, const bool sendOsc, const bool sendCallback) noexcept
{
if (pData->bridged) {
CARLA_SAFE_ASSERT_RETURN(!sendOsc && !sendCallback,);
} else {
CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
}
CARLA_SAFE_ASSERT(value >= -1.0f && value <= 1.0f);

const float fixedValue(carla_fixedValue<float>(-1.0f, 1.0f, value));
@@ -1410,20 +1421,22 @@ void CarlaPlugin::setBalanceRight(const float value, const bool sendOsc, const b

pData->postProc.balanceRight = fixedValue;

#ifdef HAVE_LIBLO
# if defined(HAVE_LIBLO) && ! defined(BUILD_BRIDGE)
if (sendOsc && pData->engine->isOscControlRegistered())
pData->engine->oscSend_control_set_parameter_value(pData->id, PARAMETER_BALANCE_RIGHT, fixedValue);
#endif
# endif

if (sendCallback)
pData->engine->callback(ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED, pData->id, PARAMETER_BALANCE_RIGHT, 0, fixedValue, nullptr);

// may be unused
return; (void)sendOsc;
}

void CarlaPlugin::setPanning(const float value, const bool sendOsc, const bool sendCallback) noexcept
{
if (pData->bridged) {
CARLA_SAFE_ASSERT_RETURN(!sendOsc && !sendCallback,);
} else {
CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
}
CARLA_SAFE_ASSERT(value >= -1.0f && value <= 1.0f);

const float fixedValue(carla_fixedValue<float>(-1.0f, 1.0f, value));
@@ -1433,16 +1446,13 @@ void CarlaPlugin::setPanning(const float value, const bool sendOsc, const bool s

pData->postProc.panning = fixedValue;

#ifdef HAVE_LIBLO
# if defined(HAVE_LIBLO) && ! defined(BUILD_BRIDGE)
if (sendOsc && pData->engine->isOscControlRegistered())
pData->engine->oscSend_control_set_parameter_value(pData->id, PARAMETER_PANNING, fixedValue);
#endif
# endif

if (sendCallback)
pData->engine->callback(ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED, pData->id, PARAMETER_PANNING, 0, fixedValue, nullptr);

// may be unused
return; (void)sendOsc;
}

void CarlaPlugin::setDryWetRT(const float value) noexcept
@@ -1508,13 +1518,15 @@ void CarlaPlugin::setPanningRT(const float value) noexcept

pData->postProc.panning = fixedValue;
}
#endif // ! BUILD_BRIDGE
#endif // ! BUILD_BRIDGE_ALTERNATIVE_ARCH

void CarlaPlugin::setCtrlChannel(const int8_t channel, const bool sendOsc, const bool sendCallback) noexcept
{
#ifndef BUILD_BRIDGE
CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
#endif
if (pData->bridged) {
CARLA_SAFE_ASSERT_RETURN(!sendOsc && !sendCallback,);
} else {
CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
}
CARLA_SAFE_ASSERT_RETURN(channel >= -1 && channel < MAX_MIDI_CHANNELS,);

if (pData->ctrlChannel == channel)
@@ -1522,20 +1534,17 @@ void CarlaPlugin::setCtrlChannel(const int8_t channel, const bool sendOsc, const

pData->ctrlChannel = channel;

#ifndef BUILD_BRIDGE
const float channelf(channel);
const float channelf = static_cast<float>(channel);

# ifdef HAVE_LIBLO
#if defined(HAVE_LIBLO) && ! defined(BUILD_BRIDGE)
if (sendOsc && pData->engine->isOscControlRegistered())
pData->engine->oscSend_control_set_parameter_value(pData->id, PARAMETER_CTRL_CHANNEL, channelf);
# endif
#endif

#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (sendCallback)
pData->engine->callback(ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED, pData->id, PARAMETER_CTRL_CHANNEL, 0, channelf, nullptr);
#endif

// may be unused
return; (void)sendOsc; (void)sendCallback;
}

// -------------------------------------------------------------------
@@ -1543,6 +1552,11 @@ void CarlaPlugin::setCtrlChannel(const int8_t channel, const bool sendOsc, const

void CarlaPlugin::setParameterValue(const uint32_t parameterId, const float value, const bool sendGui, const bool sendOsc, const bool sendCallback) noexcept
{
if (pData->bridged) {
CARLA_SAFE_ASSERT_RETURN(!sendGui && !sendOsc && !sendCallback,);
} else {
CARLA_SAFE_ASSERT_RETURN(sendGui || sendOsc || sendCallback,); // never call this from RT
}
CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count,);

if (sendGui && (pData->hints & PLUGIN_HAS_CUSTOM_UI) != 0)
@@ -1555,9 +1569,6 @@ void CarlaPlugin::setParameterValue(const uint32_t parameterId, const float valu

if (sendCallback)
pData->engine->callback(ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED, pData->id, static_cast<int>(parameterId), 0, value, nullptr);

// may be unused
return; (void)sendOsc;
}

void CarlaPlugin::setParameterValueRT(const uint32_t parameterId, const float value) noexcept
@@ -1567,7 +1578,7 @@ void CarlaPlugin::setParameterValueRT(const uint32_t parameterId, const float va

void CarlaPlugin::setParameterValueByRealIndex(const int32_t rindex, const float value, const bool sendGui, const bool sendOsc, const bool sendCallback) noexcept
{
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
CARLA_SAFE_ASSERT_RETURN(rindex > PARAMETER_MAX && rindex != PARAMETER_NULL,);

switch (rindex)
@@ -1603,16 +1614,18 @@ void CarlaPlugin::setParameterValueByRealIndex(const int32_t rindex, const float

void CarlaPlugin::setParameterMidiChannel(const uint32_t parameterId, const uint8_t channel, const bool sendOsc, const bool sendCallback) noexcept
{
#ifndef BUILD_BRIDGE
CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
#endif
if (pData->bridged) {
CARLA_SAFE_ASSERT_RETURN(!sendOsc && !sendCallback,);
} else {
CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
}
CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count,);
CARLA_SAFE_ASSERT_RETURN(channel < MAX_MIDI_CHANNELS,);

pData->param.data[parameterId].midiChannel = channel;

#ifndef BUILD_BRIDGE
# ifdef HAVE_LIBLO
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
# if defined(HAVE_LIBLO) && ! defined(BUILD_BRIDGE)
if (sendOsc && pData->engine->isOscControlRegistered())
pData->engine->oscSend_control_set_parameter_midi_channel(pData->id, parameterId, channel);
# endif
@@ -1620,23 +1633,22 @@ void CarlaPlugin::setParameterMidiChannel(const uint32_t parameterId, const uint
if (sendCallback)
pData->engine->callback(ENGINE_CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED, pData->id, static_cast<int>(parameterId), channel, 0.0f, nullptr);
#endif

// may be unused
return; (void)sendOsc; (void)sendCallback;
}

void CarlaPlugin::setParameterMidiCC(const uint32_t parameterId, const int16_t cc, const bool sendOsc, const bool sendCallback) noexcept
{
#ifndef BUILD_BRIDGE
CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
#endif
if (pData->bridged) {
CARLA_SAFE_ASSERT_RETURN(!sendOsc && !sendCallback,);
} else {
CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
}
CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count,);
CARLA_SAFE_ASSERT_RETURN(cc >= -1 && cc < MAX_MIDI_CONTROL,);

pData->param.data[parameterId].midiCC = cc;

#ifndef BUILD_BRIDGE
# ifdef HAVE_LIBLO
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
# if defined(HAVE_LIBLO) && ! defined(BUILD_BRIDGE)
if (sendOsc && pData->engine->isOscControlRegistered())
pData->engine->oscSend_control_set_parameter_midi_cc(pData->id, parameterId, cc);
# endif
@@ -1644,9 +1656,6 @@ void CarlaPlugin::setParameterMidiCC(const uint32_t parameterId, const int16_t c
if (sendCallback)
pData->engine->callback(ENGINE_CALLBACK_PARAMETER_MIDI_CC_CHANGED, pData->id, static_cast<int>(parameterId), cc, 0.0f, nullptr);
#endif

// may be unused
return; (void)sendOsc; (void)sendCallback;
}

void CarlaPlugin::setCustomData(const char* const type, const char* const key, const char* const value, const bool)
@@ -2278,7 +2287,7 @@ void CarlaPlugin::sendMidiSingleNote(const uint8_t channel, const uint8_t note,
return; (void)sendOsc;
}

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
void CarlaPlugin::sendMidiAllNotesOffToCallback()
{
if (pData->ctrlChannel < 0 || pData->ctrlChannel >= MAX_MIDI_CHANNELS)
@@ -2357,7 +2366,7 @@ void CarlaPlugin::uiIdle()
pData->postUiEvents.data.clear();
}

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (pData->transientTryCounter == 0)
return;
if (++pData->transientTryCounter % 10 != 0)
@@ -2555,7 +2564,7 @@ CarlaPlugin::ScopedSingleProcessLocker::~ScopedSingleProcessLocker() noexcept
if (! fBlock)
return;

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (fPlugin->pData->singleMutex.wasTryLockCalled())
fPlugin->pData->needsReset = true;
#endif


+ 49
- 23
source/backend/plugin/CarlaPluginBridge.cpp View File

@@ -397,7 +397,7 @@ public:
{
carla_debug("CarlaPluginBridge::~CarlaPluginBridge()");

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// close UI
if (pData->hints & PLUGIN_HAS_CUSTOM_UI)
pData->transientTryCounter = 0;
@@ -877,7 +877,7 @@ public:
fShmNonRtClientControl.commitWrite();
}

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (yesNo)
{
pData->tryTransient();
@@ -1185,7 +1185,7 @@ public:
// ----------------------------------------------------------------------------------------------------
// Event Input (System)

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
bool allNotesOffSent = false;
#endif

@@ -1208,7 +1208,7 @@ public:
break;

case kEngineControlEventTypeParameter:
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// Control backend stuff
if (event.channel == pData->ctrlChannel)
{
@@ -1295,7 +1295,7 @@ public:
case kEngineControlEventTypeAllNotesOff:
if (pData->options & PLUGIN_OPTION_SEND_ALL_SOUND_OFF)
{
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (event.channel == pData->ctrlChannel && ! allNotesOffSent)
{
allNotesOffSent = true;
@@ -1467,7 +1467,7 @@ public:
// --------------------------------------------------------------------------------------------------------
// TimeInfo

const EngineTimeInfo& timeInfo(pData->engine->getTimeInfo());
const EngineTimeInfo timeInfo(pData->engine->getTimeInfo());
BridgeTimeInfo& bridgeTimeInfo(fShmRtClientControl.data->timeInfo);

bridgeTimeInfo.playing = timeInfo.playing;
@@ -1494,6 +1494,7 @@ public:

{
fShmRtClientControl.writeOpcode(kPluginBridgeRtClientProcess);
fShmRtClientControl.writeUInt(frames);
fShmRtClientControl.commitWrite();
}

@@ -1508,7 +1509,7 @@ public:
for (uint32_t i=0; i < fInfo.aOuts; ++i)
carla_copyFloats(audioOut[i], fShmAudioPool.data + ((i + fInfo.aIns) * frames), frames);

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// --------------------------------------------------------------------------------------------------------
// Post-processing (dry/wet, volume and balance)

@@ -1530,11 +1531,13 @@ public:

for (uint32_t k=0; k < frames; ++k)
{
# ifndef BUILD_BRIDGE
if (k < pData->latency.frames)
bufValue = pData->latency.buffers[c][k];
else if (pData->latency.frames < frames)
bufValue = audioIn[c][k-pData->latency.frames];
else
# endif
bufValue = audioIn[c][k];

audioOut[i][k] = (audioOut[i][k] * pData->postProc.dryWet) + (bufValue * (1.0f - pData->postProc.dryWet));
@@ -1582,6 +1585,7 @@ public:

} // End of Post-processing

# ifndef BUILD_BRIDGE
// --------------------------------------------------------------------------------------------------------
// Save latency values for next callback

@@ -1608,8 +1612,8 @@ public:
}
}
}
#endif // BUILD_BRIDGE
# endif
#endif // BUILD_BRIDGE_ALTERNATIVE_ARCH

// --------------------------------------------------------------------------------------------------------

@@ -1761,7 +1765,7 @@ public:
{
const PluginBridgeNonRtServerOpcode opcode(fShmNonRtServerControl.readOpcode());
#ifdef DEBUG
if (opcode != kPluginBridgeNonRtServerPong) {
if (opcode != kPluginBridgeNonRtServerPong && opcode != kPluginBridgeNonRtServerParameterValue2) {
carla_debug("CarlaPluginBridge::handleNonRtData() - got opcode: %s", PluginBridgeNonRtServerOpcode2str(opcode));
}
#endif
@@ -1831,21 +1835,11 @@ public:

case kPluginBridgeNonRtServerAudioCount: {
// uint/ins, uint/outs
fInfo.clear();

fInfo.aIns = fShmNonRtServerControl.readUInt();
fInfo.aOuts = fShmNonRtServerControl.readUInt();

if (fInfo.aInNames != nullptr)
{
delete[] fInfo.aInNames;
fInfo.aInNames = nullptr;
}

if (fInfo.aOutNames != nullptr)
{
delete[] fInfo.aOutNames;
fInfo.aOutNames = nullptr;
}

if (fInfo.aIns > 0)
{
fInfo.aInNames = new const char*[fInfo.aIns];
@@ -2223,7 +2217,7 @@ public:
break;

case kPluginBridgeNonRtServerUiClosed:
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
pData->transientTryCounter = 0;
#endif
pData->engine->callback(ENGINE_CALLBACK_UI_STATE_CHANGED, pData->id, 0, 0, 0.0f, nullptr);
@@ -2521,6 +2515,38 @@ private:
aOutNames(nullptr),
chunk() {}

~Info()
{
clear();
}

void clear()
{
if (aInNames != nullptr)
{
CARLA_SAFE_ASSERT_INT(aIns > 0, aIns);

for (uint32_t i=0; i<aIns; ++i)
delete[] aInNames[i];

delete[] aInNames;
aInNames = nullptr;
}

if (aOutNames != nullptr)
{
CARLA_SAFE_ASSERT_INT(aOuts > 0, aOuts);

for (uint32_t i=0; i<aOuts; ++i)
delete[] aOutNames[i];

delete[] aOutNames;
aOutNames = nullptr;
}

aIns = aOuts = 0;
}

CARLA_DECLARE_NON_COPY_STRUCT(Info)
} fInfo;



+ 12
- 12
source/backend/plugin/CarlaPluginDSSI.cpp View File

@@ -21,7 +21,7 @@
#include "CarlaDssiUtils.hpp"
#include "CarlaMathUtils.hpp"

#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
# include "CarlaOscUtils.hpp"
# include "CarlaPipeUtils.hpp"
# include "CarlaThread.hpp"
@@ -63,7 +63,7 @@ CARLA_BACKEND_START_NAMESPACE

static const CustomData kCustomDataFallback = { nullptr, nullptr, nullptr };

#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
// -------------------------------------------------------------------

class CarlaThreadDSSIUI : public CarlaThread
@@ -283,7 +283,7 @@ public:
fForcedStereoOut(false),
fNeedsFixedBuffers(false),
fUsesCustomData(false)
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
, fOscData(),
fThreadUI(engine, this, fOscData),
fUiFilename(nullptr)
@@ -298,7 +298,7 @@ public:
{
carla_debug("CarlaPluginDSSI::~CarlaPluginDSSI()");

#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
// close UI
if (fUiFilename != nullptr)
{
@@ -594,7 +594,7 @@ public:
}
}

#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
if (sendGui && fOscData.target != nullptr)
osc_send_configure(fOscData, key, value);
#endif
@@ -684,7 +684,7 @@ public:
}
}

#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
// -------------------------------------------------------------------
// Set ui stuff

@@ -1083,7 +1083,7 @@ public:
if (LADSPA_IS_HARD_RT_CAPABLE(fDescriptor->Properties))
pData->hints |= PLUGIN_IS_RTSAFE;

#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
if (fUiFilename != nullptr)
pData->hints |= PLUGIN_HAS_CUSTOM_UI;
#endif
@@ -2184,7 +2184,7 @@ public:
carla_debug("CarlaPluginDSSI::clearBuffers() - end");
}

#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
// -------------------------------------------------------------------
// OSC stuff

@@ -2443,7 +2443,7 @@ public:
osc_send_midi(fOscData, midiData);
#endif
}
#endif // HAVE_LIBLO && !BUILD_BRIDGE_ALTERNATIVE_ARCH
#endif // HAVE_LIBLO && !BUILD_BRIDGE

// -------------------------------------------------------------------

@@ -2452,7 +2452,7 @@ public:
return fDssiDescriptor;
}

#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
uintptr_t getUiBridgeProcessId() const noexcept override
{
return fThreadUI.getProcessId();
@@ -2652,7 +2652,7 @@ public:
}
}

#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
// ---------------------------------------------------------------
// check for gui

@@ -2718,7 +2718,7 @@ private:
bool fNeedsFixedBuffers;
bool fUsesCustomData;

#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
#if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
CarlaOscData fOscData;
CarlaThreadDSSIUI fThreadUI;
const char* fUiFilename;


+ 8
- 7
source/backend/plugin/CarlaPluginInternal.cpp View File

@@ -572,7 +572,7 @@ void CarlaPlugin::ProtectedData::PostUiEvents::clear() noexcept
mutex.unlock();
}

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// -----------------------------------------------------------------------
// ProtectedData::PostProc

@@ -594,13 +594,14 @@ CarlaPlugin::ProtectedData::ProtectedData(CarlaEngine* const eng, const uint idx
options(0x0),
nodeId(0),
active(false),
bridged(eng->getType() == kEngineTypeBridge),
enabled(false),
needsReset(false),
lib(nullptr),
uiLib(nullptr),
ctrlChannel(0),
extraHints(0x0),
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
transientTryCounter(0),
transientFirstTry(true),
#endif
@@ -623,7 +624,7 @@ CarlaPlugin::ProtectedData::ProtectedData(CarlaEngine* const eng, const uint idx
latency(),
postRtEvents(),
postUiEvents()
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
, postProc()
#endif
{}
@@ -631,7 +632,7 @@ CarlaPlugin::ProtectedData::ProtectedData(CarlaEngine* const eng, const uint idx
CarlaPlugin::ProtectedData::~ProtectedData() noexcept
{
CARLA_SAFE_ASSERT(! (active && needsReset));
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
CARLA_SAFE_ASSERT(transientTryCounter == 0);
#endif

@@ -714,10 +715,10 @@ CarlaPlugin::ProtectedData::~ProtectedData() noexcept
masterMutex.unlock();
singleMutex.unlock();

CARLA_SAFE_ASSERT(uiLib == nullptr);

if (lib != nullptr)
libClose();

CARLA_SAFE_ASSERT(uiLib == nullptr);
}

// -----------------------------------------------------------------------
@@ -793,7 +794,7 @@ bool CarlaPlugin::ProtectedData::uiLibClose() noexcept

// -----------------------------------------------------------------------

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
void CarlaPlugin::ProtectedData::tryTransient() noexcept
{
if (engine->getOptions().frontendWinId != 0)


+ 4
- 3
source/backend/plugin/CarlaPluginInternal.hpp View File

@@ -217,6 +217,7 @@ struct CarlaPlugin::ProtectedData {
uint32_t nodeId;

bool active;
bool bridged;
bool enabled;
bool needsReset;

@@ -226,7 +227,7 @@ struct CarlaPlugin::ProtectedData {
// misc
int8_t ctrlChannel;
uint extraHints;
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
uint transientTryCounter;
bool transientFirstTry;
#endif
@@ -328,7 +329,7 @@ struct CarlaPlugin::ProtectedData {

} postUiEvents;

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
struct PostProc {
float dryWet;
float volume;
@@ -383,7 +384,7 @@ struct CarlaPlugin::ProtectedData {
// -------------------------------------------------------------------
// Misc

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
void tryTransient() noexcept;
#endif
void updateParameterValues(CarlaPlugin* const plugin, const bool sendOsc, const bool sendCallback, const bool useDefault) noexcept;


+ 8
- 7
source/backend/plugin/CarlaPluginJack.cpp View File

@@ -221,7 +221,7 @@ public:
{
carla_debug("CarlaPluginJack::~CarlaPluginJack()");

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// close UI
if (pData->hints & PLUGIN_HAS_CUSTOM_UI)
pData->transientTryCounter = 0;
@@ -690,7 +690,7 @@ public:
// ----------------------------------------------------------------------------------------------------
// Event Input (System)

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
bool allNotesOffSent = false;
#endif
for (uint32_t i=0, numEvents=pData->event.portIn->getEventCount(); i < numEvents; ++i)
@@ -712,7 +712,7 @@ public:
break;

case kEngineControlEventTypeParameter:
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// Control backend stuff
if (event.channel == pData->ctrlChannel)
{
@@ -793,7 +793,7 @@ public:
case kEngineControlEventTypeAllNotesOff:
if (pData->options & PLUGIN_OPTION_SEND_ALL_SOUND_OFF)
{
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (event.channel == pData->ctrlChannel && ! allNotesOffSent)
{
allNotesOffSent = true;
@@ -940,7 +940,7 @@ public:
// --------------------------------------------------------------------------------------------------------
// TimeInfo

const EngineTimeInfo& timeInfo(pData->engine->getTimeInfo());
const EngineTimeInfo timeInfo(pData->engine->getTimeInfo());
BridgeTimeInfo& bridgeTimeInfo(fShmRtClientControl.data->timeInfo);

bridgeTimeInfo.playing = timeInfo.playing;
@@ -967,6 +967,7 @@ public:

{
fShmRtClientControl.writeOpcode(kPluginBridgeRtClientProcess);
fShmRtClientControl.writeUInt(frames);
fShmRtClientControl.commitWrite();
}

@@ -987,7 +988,7 @@ public:
for (uint32_t i=0; i < fInfo.aOuts; ++i)
carla_copyFloats(audioOut[i], fShmAudioPool.data + ((i + fInfo.aIns) * frames), frames);

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// --------------------------------------------------------------------------------------------------------
// Post-processing (dry/wet, volume and balance)

@@ -1310,7 +1311,7 @@ public:

// FIXME dryWet broken
pData->hints = PLUGIN_IS_BRIDGE | PLUGIN_OPTION_FIXED_BUFFERS;
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
pData->hints |= /*PLUGIN_CAN_DRYWET |*/ PLUGIN_CAN_VOLUME | PLUGIN_CAN_BALANCE;
#endif
//fInfo.optionsAvailable = optionAv;


+ 8
- 5
source/backend/plugin/CarlaPluginLADSPA.cpp View File

@@ -776,7 +776,7 @@ public:
if (LADSPA_IS_HARD_RT_CAPABLE(fDescriptor->Properties))
pData->hints |= PLUGIN_IS_RTSAFE;

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (aOuts > 0 && (aIns == aOuts || aIns == 1))
pData->hints |= PLUGIN_CAN_DRYWET;

@@ -971,7 +971,7 @@ public:
break;

case kEngineControlEventTypeParameter: {
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// Control backend stuff
if (event.channel == pData->ctrlChannel)
{
@@ -1194,7 +1194,7 @@ public:
carla_copyFloats(fAudioOutBuffers[1], fExtraStereoBuffer[1], frames);
}

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// --------------------------------------------------------------------------------------------------------
// Post-processing (dry/wet, volume and balance)

@@ -1215,11 +1215,13 @@ public:

for (uint32_t k=0; k < frames; ++k)
{
# ifndef BUILD_BRIDGE
if (k < pData->latency.frames)
bufValue = pData->latency.buffers[c][k];
else if (pData->latency.frames < frames)
bufValue = fAudioInBuffers[c][k-pData->latency.frames];
else
# endif
bufValue = fAudioInBuffers[c][k];

fAudioOutBuffers[i][k] = (fAudioOutBuffers[i][k] * pData->postProc.dryWet) + (bufValue * (1.0f - pData->postProc.dryWet));
@@ -1266,6 +1268,7 @@ public:

} // End of Post-processing

# ifndef BUILD_BRIDGE
// --------------------------------------------------------------------------------------------------------
// Save latency values for next callback

@@ -1294,8 +1297,8 @@ public:
}
}
}
#else // BUILD_BRIDGE
# endif
#else // BUILD_BRIDGE_ALTERNATIVE_ARCH
for (uint32_t i=0; i < pData->audioOut.count; ++i)
{
for (uint32_t k=0; k < frames; ++k)


+ 20
- 17
source/backend/plugin/CarlaPluginLV2.cpp View File

@@ -1274,7 +1274,7 @@ public:

const uintptr_t frontendWinId(pData->engine->getOptions().frontendWinId);

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (! yesNo)
pData->transientTryCounter = 0;
#endif
@@ -1486,7 +1486,7 @@ public:
else if (fExt.uishow != nullptr)
{
fExt.uishow->show(fUI.handle);
# ifndef BUILD_BRIDGE
# ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
pData->tryTransient();
# endif
}
@@ -1495,7 +1495,7 @@ public:
#endif
{
LV2_EXTERNAL_UI_SHOW((LV2_External_UI_Widget*)fUI.widget);
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
pData->tryTransient();
#endif
}
@@ -1570,7 +1570,7 @@ public:
fPipeServer.stopPipeServer(2000);
// fall through
case CarlaPipeServerLV2::UiCrashed:
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
pData->transientTryCounter = 0;
#endif
pData->engine->callback(ENGINE_CALLBACK_UI_STATE_CHANGED, pData->id, 0, 0, 0.0f, nullptr);
@@ -2779,7 +2779,7 @@ public:
// --------------------------------------------------------------------------------------------------------
// TimeInfo

const EngineTimeInfo& timeInfo(pData->engine->getTimeInfo());
const EngineTimeInfo timeInfo(pData->engine->getTimeInfo());

if (fFirstActive || fLastTimeInfo != timeInfo)
{
@@ -3023,7 +3023,7 @@ public:
// ----------------------------------------------------------------------------------------------------
// Event Input (System)

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
bool allNotesOffSent = false;
#endif
bool isSampleAccurate = (pData->options & PLUGIN_OPTION_FIXED_BUFFERS) == 0;
@@ -3121,7 +3121,7 @@ public:
break;

case kEngineControlEventTypeParameter: {
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// Control backend stuff
if (event.channel == pData->ctrlChannel)
{
@@ -3306,7 +3306,7 @@ public:
case kEngineControlEventTypeAllNotesOff:
if (pData->options & PLUGIN_OPTION_SEND_ALL_SOUND_OFF)
{
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (event.channel == pData->ctrlChannel && ! allNotesOffSent)
{
allNotesOffSent = true;
@@ -3503,7 +3503,7 @@ public:
}
}

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// --------------------------------------------------------------------------------------------------------
// Control Output

@@ -3642,7 +3642,7 @@ public:

pData->postRtEvents.trySplice();

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// --------------------------------------------------------------------------------------------------------
// Post-processing (dry/wet, volume and balance)

@@ -3663,11 +3663,13 @@ public:

for (uint32_t k=0; k < frames; ++k)
{
# ifndef BUILD_BRIDGE
if (k < pData->latency.frames)
bufValue = pData->latency.buffers[c][k];
else if (pData->latency.frames < frames)
bufValue = fAudioInBuffers[c][k-pData->latency.frames];
else
# endif
bufValue = fAudioInBuffers[c][k];

fAudioOutBuffers[i][k] = (fAudioOutBuffers[i][k] * pData->postProc.dryWet) + (bufValue * (1.0f - pData->postProc.dryWet));
@@ -3713,6 +3715,7 @@ public:
}
} // End of Post-processing

# ifndef BUILD_BRIDGE
// --------------------------------------------------------------------------------------------------------
// Save latency values for next callback

@@ -3741,8 +3744,8 @@ public:
}
}
}
#else // BUILD_BRIDGE
# endif
#else // BUILD_BRIDGE_ALTERNATIVE_ARCH
for (uint32_t i=0; i < pData->audioOut.count; ++i)
{
for (uint32_t k=0; k < frames; ++k)
@@ -4853,7 +4856,7 @@ public:
{
if (pData->param.data[i].rindex == rindex)
{
setParameterValue(i, paramValue, true, true, true);
setParameterValueRT(i, paramValue);
break;
}
}
@@ -5287,7 +5290,7 @@ public:

#if defined(LV2_UIS_ONLY_BRIDGES)
const bool preferUiBridges = true;
#elif defined(BUILD_BRIDGE)
#elif defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
const bool preferUiBridges = false;
#else
const bool preferUiBridges = pData->engine->getOptions().preferUiBridges;
@@ -5383,7 +5386,7 @@ public:
else if (iExt >= 0)
iFinal = iExt;

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (iFinal < 0)
#endif
{
@@ -5459,7 +5462,7 @@ public:
iFinal == eCocoa ||
iFinal == eWindows ||
iFinal == eX11)
#ifdef BUILD_BRIDGE
#ifdef BUILD_BRIDGE_ALTERNATIVE_ARCH
&& ! hasShowInterface
#endif
)
@@ -6273,7 +6276,7 @@ private:
static void carla_lv2_inline_display_queue_draw(LV2_Inline_Display_Handle handle)
{
CARLA_SAFE_ASSERT_RETURN(handle != nullptr,);
carla_debug("carla_lv2_inline_display_queue_draw(%p)", handle);
// carla_debug("carla_lv2_inline_display_queue_draw(%p)", handle);

((CarlaPluginLV2*)handle)->handleInlineDisplayQueueRedraw();
}


+ 35
- 2
source/backend/plugin/CarlaPluginNative.cpp View File

@@ -708,7 +708,7 @@ public:
CARLA_SAFE_ASSERT_RETURN(type != nullptr && type[0] != '\0',);
CARLA_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',);
CARLA_SAFE_ASSERT_RETURN(value != nullptr,);
carla_debug("CarlaPluginNative::setCustomData(%s, %s, %s, %s)", type, key, value, bool2str(sendGui));
carla_debug("CarlaPluginNative::setCustomData(%s, %s, ..., %s)", type, key, bool2str(sendGui));

if (std::strcmp(type, CUSTOM_DATA_TYPE_PROPERTY) == 0)
return CarlaPlugin::setCustomData(type, key, value, sendGui);
@@ -1537,7 +1537,7 @@ public:
// --------------------------------------------------------------------------------------------------------
// Set TimeInfo

const EngineTimeInfo& timeInfo(pData->engine->getTimeInfo());
const EngineTimeInfo timeInfo(pData->engine->getTimeInfo());

fTimeInfo.playing = timeInfo.playing;
fTimeInfo.frame = timeInfo.frame;
@@ -1563,6 +1563,39 @@ public:
fTimeInfo.bbt.valid = false;
}

#if 0
// This test code has proven to be quite useful
// So I am leaving it behind, I might need it again..
if (pData->id == 1)
{
static int64_t last_frame = timeInfo.frame;
static int64_t last_dev_frame = 0;
static double last_val = timeInfo.bbt.barStartTick + ((timeInfo.bbt.beat-1) * timeInfo.bbt.ticksPerBeat) + timeInfo.bbt.tick;
static double last_dev_val = 0.0;

int64_t cur_frame = timeInfo.frame;
int64_t cur_dev_frame = cur_frame - last_frame;

double cur_val = timeInfo.bbt.barStartTick + ((timeInfo.bbt.beat-1) * timeInfo.bbt.ticksPerBeat) + timeInfo.bbt.tick;
double cur_dev_val = cur_val - last_val;

if (std::abs(last_dev_val - cur_dev_val) >= 0.0001 || last_dev_frame != cur_dev_frame)
{
carla_stdout("currently %u at %u => %f : DEV1: %li : DEV2: %f",
frames,
timeInfo.frame,
cur_val,
cur_dev_frame,
cur_dev_val);
}

last_val = cur_val;
last_dev_val = cur_dev_val;
last_frame = cur_frame;
last_dev_frame = cur_dev_frame;
}
#endif

// --------------------------------------------------------------------------------------------------------
// Event Input and Processing



+ 5
- 5
source/backend/plugin/CarlaPluginSFZero.cpp View File

@@ -363,7 +363,7 @@ public:
// ----------------------------------------------------------------------------------------------------
// Event Input (System)

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
bool allNotesOffSent = false;
#endif
uint32_t timeOffset = 0;
@@ -404,7 +404,7 @@ public:

case kEngineControlEventTypeParameter:
{
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// Control backend stuff
if (event.channel == pData->ctrlChannel)
{
@@ -493,7 +493,7 @@ public:
case kEngineControlEventTypeAllNotesOff:
if (pData->options & PLUGIN_OPTION_SEND_ALL_SOUND_OFF)
{
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (event.channel == pData->ctrlChannel && ! allNotesOffSent)
{
allNotesOffSent = true;
@@ -583,13 +583,13 @@ public:

fSynth.renderVoices(audioOutBuffer, timeOffset, frames);

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// --------------------------------------------------------------------------------------------------------
// Post-processing (dry/wet, volume and balance)

{
const bool doVolume = carla_isNotEqual(pData->postProc.volume, 1.0f);
const bool doBalance = carla_isNotEqual(pData->postProc.balanceLeft, -1.0f) || carla_isNotEqual(pData->postProc.balanceRight, 1.0f);
//const bool doBalance = carla_isNotEqual(pData->postProc.balanceLeft, -1.0f) || carla_isNotEqual(pData->postProc.balanceRight, 1.0f);

float* outBufferL = audioOutBuffer.getWritePointer(0, timeOffset);
float* outBufferR = audioOutBuffer.getWritePointer(1, timeOffset);


+ 20
- 13
source/backend/plugin/CarlaPluginVST2.cpp View File

@@ -481,14 +481,16 @@ public:
const char* msg = nullptr;
const uintptr_t frontendWinId(pData->engine->getOptions().frontendWinId);

#if defined(CARLA_OS_MAC) && defined(CARLA_OS_64BIT)
#if defined(CARLA_OS_MAC)
fUI.window = CarlaPluginUI::newCocoa(this, frontendWinId, false);
#elif defined(CARLA_OS_WIN)
fUI.window = CarlaPluginUI::newWindows(this, frontendWinId, false);
#elif defined(HAVE_X11)
fUI.window = CarlaPluginUI::newX11(this, frontendWinId, false);
#else
msg = "Unknown UI type";
msg = "Unsupported UI type";
// unused
(void)frontendWinId;
#endif

if (fUI.window == nullptr)
@@ -862,7 +864,13 @@ public:

if (fEffect->flags & effFlagsHasEditor)
{
pData->hints |= PLUGIN_HAS_CUSTOM_UI;
#ifndef CARLA_OS_64BIT
if (static_cast<uintptr_t>(dispatcher(effCanDo, 0, 0, const_cast<char*>("hasCockosViewAsConfig")) & 0xffff0000) == 0xbeef0000)
#endif
{
pData->hints |= PLUGIN_HAS_CUSTOM_UI;
}

pData->hints |= PLUGIN_NEEDS_UI_MAIN_THREAD;
}

@@ -872,7 +880,7 @@ public:
if ((fEffect->flags & effFlagsCanReplacing) != 0 && fEffect->processReplacing != fEffect->process)
pData->hints |= PLUGIN_CAN_PROCESS_REPLACING;

if (static_cast<uintptr_t>(dispatcher(effCanDo, 0, 0, const_cast<char*>("hasCockosExtensions"), 0.0f)) == 0xbeef0000)
if (static_cast<uintptr_t>(dispatcher(effCanDo, 0, 0, const_cast<char*>("hasCockosExtensions"))) == 0xbeef0000)
pData->hints |= PLUGIN_HAS_COCKOS_EXTENSIONS;

if (aOuts > 0 && (aIns == aOuts || aIns == 1))
@@ -1113,7 +1121,7 @@ public:
// --------------------------------------------------------------------------------------------------------
// Set TimeInfo

const EngineTimeInfo& timeInfo(pData->engine->getTimeInfo());
const EngineTimeInfo timeInfo(pData->engine->getTimeInfo());

fTimeInfo.flags = kVstTransportChanged;

@@ -1205,7 +1213,7 @@ public:
// ----------------------------------------------------------------------------------------------------
// Event Input (System)

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
bool allNotesOffSent = false;
#endif
bool isSampleAccurate = (pData->options & PLUGIN_OPTION_FIXED_BUFFERS) == 0;
@@ -1258,7 +1266,7 @@ public:
break;

case kEngineControlEventTypeParameter: {
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// Control backend stuff
if (event.channel == pData->ctrlChannel)
{
@@ -1417,7 +1425,7 @@ public:
case kEngineControlEventTypeAllNotesOff:
if (pData->options & PLUGIN_OPTION_SEND_ALL_SOUND_OFF)
{
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
if (event.channel == pData->ctrlChannel && ! allNotesOffSent)
{
allNotesOffSent = true;
@@ -1608,7 +1616,7 @@ public:
fIsProcessing = false;
fTimeInfo.samplePos += frames;

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
// --------------------------------------------------------------------------------------------------------
// Post-processing (dry/wet, volume and balance)

@@ -2085,7 +2093,9 @@ protected:
}
}

pData->engine->callback(ENGINE_CALLBACK_UPDATE, pData->id, 0, 0, 0.0f, nullptr);
if (! fIsInitializing)
pData->engine->callback(ENGINE_CALLBACK_UPDATE, pData->id, 0, 0, 0.0f, nullptr);

ret = 1;
break;

@@ -2357,9 +2367,6 @@ public:
}

return true;

// unused
(void)uniqueId;
}

private:


+ 576
- 0
source/backend/utils/CachedPlugins.cpp View File

@@ -0,0 +1,576 @@
/*
* Carla Plugin Host
* Copyright (C) 2011-2018 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

#include "CarlaUtils.h"

#include "CarlaNative.h"
#include "CarlaString.hpp"
#include "CarlaLv2Utils.hpp"

#ifdef DEBUG
# include "CarlaBackendUtils.hpp"
#endif

#include "water/containers/Array.h"
#include "water/files/File.h"

namespace CB = CarlaBackend;

using water::Array;
using water::File;
using water::String;
using water::StringArray;

static const char* const gNullCharPtr = "";

static bool isCachedPluginType(const CB::PluginType ptype)
{
switch (ptype)
{
case CB::PLUGIN_INTERNAL:
case CB::PLUGIN_LV2:
case CB::PLUGIN_SFZ:
return true;
default:
return false;
}
}

// -------------------------------------------------------------------------------------------------------------------

_CarlaCachedPluginInfo::_CarlaCachedPluginInfo() noexcept
: valid(false),
category(CB::PLUGIN_CATEGORY_NONE),
hints(0x0),
audioIns(0),
audioOuts(0),
midiIns(0),
midiOuts(0),
parameterIns(0),
parameterOuts(0),
name(gNullCharPtr),
label(gNullCharPtr),
maker(gNullCharPtr),
copyright(gNullCharPtr) {}

// -------------------------------------------------------------------------------------------------------------------

static Array<File> gSFZs;

void findSFZsIfNeeded(const char* const sfzPaths)
{
CARLA_SAFE_ASSERT_RETURN(sfzPaths != nullptr && sfzPaths[0] != '\0',);

static bool needsInit = true;

if (! needsInit)
return;
needsInit = false;

const StringArray splitPaths(StringArray::fromTokens(sfzPaths, CARLA_OS_SPLIT_STR, ""));

for (String *it = splitPaths.begin(), *end = splitPaths.end(); it != end; ++it)
{
Array<File> results;

if (File(*it).findChildFiles(results, File::findFiles|File::ignoreHiddenFiles, true, "*.sfz") > 0)
gSFZs.addArray(results);
}
}

// -------------------------------------------------------------------------------------------------------------------

const CarlaCachedPluginInfo* get_cached_plugin_internal(const NativePluginDescriptor& desc)
{
static CarlaCachedPluginInfo info;

info.category = static_cast<CB::PluginCategory>(desc.category);
info.hints = 0x0;

if (desc.hints & NATIVE_PLUGIN_IS_RTSAFE)
info.hints |= CB::PLUGIN_IS_RTSAFE;
if (desc.hints & NATIVE_PLUGIN_IS_SYNTH)
info.hints |= CB::PLUGIN_IS_SYNTH;
if (desc.hints & NATIVE_PLUGIN_HAS_UI)
info.hints |= CB::PLUGIN_HAS_CUSTOM_UI;
if (desc.hints & NATIVE_PLUGIN_NEEDS_FIXED_BUFFERS)
info.hints |= CB::PLUGIN_NEEDS_FIXED_BUFFERS;
if (desc.hints & NATIVE_PLUGIN_NEEDS_UI_MAIN_THREAD)
info.hints |= CB::PLUGIN_NEEDS_UI_MAIN_THREAD;
if (desc.hints & NATIVE_PLUGIN_USES_MULTI_PROGS)
info.hints |= CB::PLUGIN_USES_MULTI_PROGS;

info.valid = true;
info.audioIns = desc.audioIns;
info.audioOuts = desc.audioOuts;
info.midiIns = desc.midiIns;
info.midiOuts = desc.midiOuts;
info.parameterIns = desc.paramIns;
info.parameterOuts = desc.paramOuts;
info.name = desc.name;
info.label = desc.label;
info.maker = desc.maker;
info.copyright = desc.copyright;
return &info;
}

const CarlaCachedPluginInfo* get_cached_plugin_lv2(Lv2WorldClass& lv2World, Lilv::Plugin& lilvPlugin)
{
static CarlaCachedPluginInfo info;

info.valid = false;
bool supported = true;

// ----------------------------------------------------------------------------------------------------------------
// text data

{
static CarlaString suri, sname, smaker, slicense;
suri.clear(); sname.clear(); smaker.clear(); slicense.clear();

suri = lilvPlugin.get_uri().as_uri();

if (LilvNode* const nameNode = lilv_plugin_get_name(lilvPlugin.me))
{
if (const char* const name = lilv_node_as_string(nameNode))
sname = name;
lilv_node_free(nameNode);
}

if (const char* const author = lilvPlugin.get_author_name().as_string())
smaker = author;

Lilv::Nodes licenseNodes(lilvPlugin.get_value(lv2World.doap_license));

if (licenseNodes.size() > 0)
{
if (const char* const license = licenseNodes.get_first().as_string())
slicense = license;
}

lilv_nodes_free(const_cast<LilvNodes*>(licenseNodes.me));

info.name = sname.buffer();
info.label = suri.buffer();
info.maker = smaker.buffer();
info.copyright = slicense.buffer();
}

// ----------------------------------------------------------------------------------------------------------------
// features

info.hints = 0x0;

if (lilvPlugin.get_uis().size() > 0)
info.hints |= CB::PLUGIN_HAS_CUSTOM_UI;

{
Lilv::Nodes lilvRequiredFeatureNodes(lilvPlugin.get_required_features());

LILV_FOREACH(nodes, it, lilvRequiredFeatureNodes)
{
Lilv::Node lilvFeatureNode(lilvRequiredFeatureNodes.get(it));
const char* const featureURI(lilvFeatureNode.as_uri());
CARLA_SAFE_ASSERT_CONTINUE(featureURI != nullptr);

if (! is_lv2_feature_supported(featureURI))
{
if (std::strcmp(featureURI, LV2_DATA_ACCESS_URI) == 0
|| std::strcmp(featureURI, LV2_INSTANCE_ACCESS_URI) == 0)
{
// we give a warning about this below
continue;
}

supported = false;
carla_stderr("LV2 plugin '%s' requires unsupported feature '%s'", info.label, featureURI);
}
}

lilv_nodes_free(const_cast<LilvNodes*>(lilvRequiredFeatureNodes.me));
}

{
Lilv::Nodes lilvSupportedFeatureNodes(lilvPlugin.get_supported_features());

LILV_FOREACH(nodes, it, lilvSupportedFeatureNodes)
{
Lilv::Node lilvFeatureNode(lilvSupportedFeatureNodes.get(it));
const char* const featureURI(lilvFeatureNode.as_uri());
CARLA_SAFE_ASSERT_CONTINUE(featureURI != nullptr);

if (std::strcmp(featureURI, LV2_CORE__hardRTCapable) == 0)
{
info.hints |= CB::PLUGIN_IS_RTSAFE;
}
else if (std::strcmp(featureURI, LV2_DATA_ACCESS_URI) == 0
|| std::strcmp(featureURI, LV2_INSTANCE_ACCESS_URI) == 0)
{
carla_stderr("LV2 plugin '%s' DSP wants UI feature '%s', ignoring this", info.label, featureURI);
}
}

lilv_nodes_free(const_cast<LilvNodes*>(lilvSupportedFeatureNodes.me));
}

// ----------------------------------------------------------------------------------------------------------------
// category

info.category = CB::PLUGIN_CATEGORY_NONE;

{
Lilv::Nodes typeNodes(lilvPlugin.get_value(lv2World.rdf_type));

if (typeNodes.size() > 0)
{
if (typeNodes.contains(lv2World.class_allpass))
info.category = CB::PLUGIN_CATEGORY_FILTER;
if (typeNodes.contains(lv2World.class_amplifier))
info.category = CB::PLUGIN_CATEGORY_DYNAMICS;
if (typeNodes.contains(lv2World.class_analyzer))
info.category = CB::PLUGIN_CATEGORY_UTILITY;
if (typeNodes.contains(lv2World.class_bandpass))
info.category = CB::PLUGIN_CATEGORY_FILTER;
if (typeNodes.contains(lv2World.class_chorus))
info.category = CB::PLUGIN_CATEGORY_MODULATOR;
if (typeNodes.contains(lv2World.class_comb))
info.category = CB::PLUGIN_CATEGORY_FILTER;
if (typeNodes.contains(lv2World.class_compressor))
info.category = CB::PLUGIN_CATEGORY_DYNAMICS;
if (typeNodes.contains(lv2World.class_constant))
info.category = CB::PLUGIN_CATEGORY_OTHER;
if (typeNodes.contains(lv2World.class_converter))
info.category = CB::PLUGIN_CATEGORY_UTILITY;
if (typeNodes.contains(lv2World.class_delay))
info.category = CB::PLUGIN_CATEGORY_DELAY;
if (typeNodes.contains(lv2World.class_distortion))
info.category = CB::PLUGIN_CATEGORY_DISTORTION;
if (typeNodes.contains(lv2World.class_dynamics))
info.category = CB::PLUGIN_CATEGORY_DYNAMICS;
if (typeNodes.contains(lv2World.class_eq))
info.category = CB::PLUGIN_CATEGORY_EQ;
if (typeNodes.contains(lv2World.class_envelope))
info.category = CB::PLUGIN_CATEGORY_DYNAMICS;
if (typeNodes.contains(lv2World.class_expander))
info.category = CB::PLUGIN_CATEGORY_DYNAMICS;
if (typeNodes.contains(lv2World.class_filter))
info.category = CB::PLUGIN_CATEGORY_FILTER;
if (typeNodes.contains(lv2World.class_flanger))
info.category = CB::PLUGIN_CATEGORY_MODULATOR;
if (typeNodes.contains(lv2World.class_function))
info.category = CB::PLUGIN_CATEGORY_UTILITY;
if (typeNodes.contains(lv2World.class_gate))
info.category = CB::PLUGIN_CATEGORY_DYNAMICS;
if (typeNodes.contains(lv2World.class_generator))
info.category = CB::PLUGIN_CATEGORY_OTHER;
if (typeNodes.contains(lv2World.class_highpass))
info.category = CB::PLUGIN_CATEGORY_FILTER;
if (typeNodes.contains(lv2World.class_limiter))
info.category = CB::PLUGIN_CATEGORY_DYNAMICS;
if (typeNodes.contains(lv2World.class_lowpass))
info.category = CB::PLUGIN_CATEGORY_FILTER;
if (typeNodes.contains(lv2World.class_mixer))
info.category = CB::PLUGIN_CATEGORY_UTILITY;
if (typeNodes.contains(lv2World.class_modulator))
info.category = CB::PLUGIN_CATEGORY_MODULATOR;
if (typeNodes.contains(lv2World.class_multiEQ))
info.category = CB::PLUGIN_CATEGORY_EQ;
if (typeNodes.contains(lv2World.class_oscillator))
info.category = CB::PLUGIN_CATEGORY_OTHER;
if (typeNodes.contains(lv2World.class_paraEQ))
info.category = CB::PLUGIN_CATEGORY_EQ;
if (typeNodes.contains(lv2World.class_phaser))
info.category = CB::PLUGIN_CATEGORY_MODULATOR;
if (typeNodes.contains(lv2World.class_pitch))
info.category = CB::PLUGIN_CATEGORY_OTHER;
if (typeNodes.contains(lv2World.class_reverb))
info.category = CB::PLUGIN_CATEGORY_DELAY;
if (typeNodes.contains(lv2World.class_simulator))
info.category = CB::PLUGIN_CATEGORY_OTHER;
if (typeNodes.contains(lv2World.class_spatial))
info.category = CB::PLUGIN_CATEGORY_OTHER;
if (typeNodes.contains(lv2World.class_spectral))
info.category = CB::PLUGIN_CATEGORY_OTHER;
if (typeNodes.contains(lv2World.class_utility))
info.category = CB::PLUGIN_CATEGORY_UTILITY;
if (typeNodes.contains(lv2World.class_waveshaper))
info.category = CB::PLUGIN_CATEGORY_DISTORTION;
if (typeNodes.contains(lv2World.class_instrument))
{
info.category = CB::PLUGIN_CATEGORY_SYNTH;
info.hints |= CB::PLUGIN_IS_SYNTH;
}
}

lilv_nodes_free(const_cast<LilvNodes*>(typeNodes.me));
}

// ----------------------------------------------------------------------------------------------------------------
// number data

for (uint i=0, count=lilvPlugin.get_num_ports(); i<count; ++i)
{
Lilv::Port lilvPort(lilvPlugin.get_port_by_index(i));

bool isInput;

/**/ if (lilvPort.is_a(lv2World.port_input))
{
isInput = true;
}
else if (lilvPort.is_a(lv2World.port_output))
{
isInput = false;
}
else
{
const LilvNode* const symbolNode = lilvPort.get_symbol();
CARLA_SAFE_ASSERT_CONTINUE(symbolNode != nullptr && lilv_node_is_string(symbolNode));

const char* const symbol = lilv_node_as_string(symbolNode);
CARLA_SAFE_ASSERT_CONTINUE(symbol != nullptr);

carla_stderr("LV2 plugin '%s' port '%s' is neither input or output", info.label, symbol);
continue;
}

/**/ if (lilvPort.is_a(lv2World.port_control))
{
// skip some control ports
if (lilvPort.has_property(lv2World.reportsLatency))
continue;

if (LilvNode* const designationNode = lilv_port_get(lilvPort.parent, lilvPort.me, lv2World.designation.me))
{
bool skip = false;

if (const char* const designation = lilv_node_as_string(designationNode))
{
/**/ if (std::strcmp(designation, LV2_CORE__control) == 0)
skip = true;
else if (std::strcmp(designation, LV2_CORE__freeWheeling) == 0)
skip = true;
else if (std::strcmp(designation, LV2_CORE__latency) == 0)
skip = true;
else if (std::strcmp(designation, LV2_PARAMETERS__sampleRate) == 0)
skip = true;
else if (std::strcmp(designation, LV2_TIME__bar) == 0)
skip = true;
else if (std::strcmp(designation, LV2_TIME__barBeat) == 0)
skip = true;
else if (std::strcmp(designation, LV2_TIME__beat) == 0)
skip = true;
else if (std::strcmp(designation, LV2_TIME__beatUnit) == 0)
skip = true;
else if (std::strcmp(designation, LV2_TIME__beatsPerBar) == 0)
skip = true;
else if (std::strcmp(designation, LV2_TIME__beatsPerMinute) == 0)
skip = true;
else if (std::strcmp(designation, LV2_TIME__frame) == 0)
skip = true;
else if (std::strcmp(designation, LV2_TIME__framesPerSecond) == 0)
skip = true;
else if (std::strcmp(designation, LV2_TIME__speed) == 0)
skip = true;
else if (std::strcmp(designation, LV2_KXSTUDIO_PROPERTIES__TimePositionTicksPerBeat) == 0)
skip = true;
}

lilv_node_free(designationNode);

if (skip)
continue;
}

if (isInput)
++(info.parameterIns);
else
++(info.parameterOuts);
}
else if (lilvPort.is_a(lv2World.port_audio))
{
if (isInput)
++(info.audioIns);
else
++(info.audioOuts);
}
else if (lilvPort.is_a(lv2World.port_cv))
{
}
else if (lilvPort.is_a(lv2World.port_atom))
{
Lilv::Nodes supportNodes(lilvPort.get_value(lv2World.atom_supports));

for (LilvIter *it = lilv_nodes_begin(supportNodes.me); ! lilv_nodes_is_end(supportNodes.me, it); it = lilv_nodes_next(supportNodes.me, it))
{
const Lilv::Node node(lilv_nodes_get(supportNodes.me, it));
CARLA_SAFE_ASSERT_CONTINUE(node.is_uri());

if (node.equals(lv2World.midi_event))
{
if (isInput)
++(info.midiIns);
else
++(info.midiOuts);
}
}

lilv_nodes_free(const_cast<LilvNodes*>(supportNodes.me));
}
else if (lilvPort.is_a(lv2World.port_event))
{
if (lilvPort.supports_event(lv2World.midi_event))
{
if (isInput)
++(info.midiIns);
else
++(info.midiOuts);
}
}
else if (lilvPort.is_a(lv2World.port_midi))
{
if (isInput)
++(info.midiIns);
else
++(info.midiOuts);
}
else
{
const LilvNode* const symbolNode = lilvPort.get_symbol();
CARLA_SAFE_ASSERT_CONTINUE(symbolNode != nullptr && lilv_node_is_string(symbolNode));

const char* const symbol = lilv_node_as_string(symbolNode);
CARLA_SAFE_ASSERT_CONTINUE(symbol != nullptr);

supported = false;
carla_stderr("LV2 plugin '%s' port '%s' is required but has unsupported type", info.label, symbol);
}
}

if (supported)
info.valid = true;

return &info;
}

const CarlaCachedPluginInfo* get_cached_plugin_sfz(const File file)
{
static CarlaCachedPluginInfo info;

static CarlaString name, filename;

name = file.getFileNameWithoutExtension().toRawUTF8();
name.replace('_',' ');

filename = file.getFullPathName().toRawUTF8();

info.category = CB::PLUGIN_CATEGORY_SYNTH;
info.hints = CB::PLUGIN_IS_SYNTH;
// CB::PLUGIN_IS_RTSAFE

info.valid = true;
info.audioIns = 0;
info.audioOuts = 2;
info.midiIns = 1;
info.midiOuts = 0;
info.parameterIns = 0;
info.parameterOuts = 1;

info.name = name.buffer();
info.label = filename.buffer();
info.maker = gNullCharPtr;
info.copyright = gNullCharPtr;
return &info;
}

// -------------------------------------------------------------------------------------------------------------------

uint carla_get_cached_plugin_count(CB::PluginType ptype, const char* pluginPath)
{
CARLA_SAFE_ASSERT_RETURN(isCachedPluginType(ptype), 0);
carla_debug("carla_get_cached_plugin_count(%i:%s, %s)", ptype, CB::PluginType2Str(ptype), pluginPath);

switch (ptype)
{
case CB::PLUGIN_INTERNAL: {
uint32_t count = 0;
carla_get_native_plugins_data(&count);
return count;
}

case CB::PLUGIN_LV2: {
Lv2WorldClass& lv2World(Lv2WorldClass::getInstance());
lv2World.initIfNeeded(pluginPath);
return lv2World.getPluginCount();
}

case CB::PLUGIN_SFZ: {
findSFZsIfNeeded(pluginPath);
return gSFZs.size();
}

default:
return 0;
}
}

const CarlaCachedPluginInfo* carla_get_cached_plugin_info(CB::PluginType ptype, uint index)
{
carla_debug("carla_get_cached_plugin_info(%i:%s, %i)", ptype, CB::PluginType2Str(ptype), index);

switch (ptype)
{
case CB::PLUGIN_INTERNAL: {
uint32_t count = 0;
const NativePluginDescriptor* const descs(carla_get_native_plugins_data(&count));
CARLA_SAFE_ASSERT_BREAK(index < count);
CARLA_SAFE_ASSERT_BREAK(descs != nullptr);

const NativePluginDescriptor& desc(descs[index]);
return get_cached_plugin_internal(desc);
}

case CB::PLUGIN_LV2: {
Lv2WorldClass& lv2World(Lv2WorldClass::getInstance());

const LilvPlugin* const cPlugin(lv2World.getPluginFromIndex(index));
CARLA_SAFE_ASSERT_BREAK(cPlugin != nullptr);

Lilv::Plugin lilvPlugin(cPlugin);
CARLA_SAFE_ASSERT_BREAK(lilvPlugin.get_uri().is_uri());

return get_cached_plugin_lv2(lv2World, lilvPlugin);
}

case CB::PLUGIN_SFZ: {
CARLA_SAFE_ASSERT_BREAK(index < static_cast<uint>(gSFZs.size()));
return get_cached_plugin_sfz(gSFZs.getUnchecked(index));
}

default:
break;
}

static CarlaCachedPluginInfo info;
return &info;
}

// -------------------------------------------------------------------------------------------------------------------

#include "../native-plugins/_data.all.cpp"

// -------------------------------------------------------------------------------------------------------------------

+ 158
- 0
source/backend/utils/Information.cpp View File

@@ -0,0 +1,158 @@
/*
* Carla Plugin Host
* Copyright (C) 2011-2018 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

#include "CarlaUtils.h"

#include "CarlaString.hpp"

#include "rtaudio/RtAudio.h"
#include "rtmidi/RtMidi.h"

#include "water/files/File.h"

// -------------------------------------------------------------------------------------------------------------------

const char* carla_get_complete_license_text()
{
carla_debug("carla_get_complete_license_text()");

static CarlaString retText;

if (retText.isEmpty())
{
retText =
"<p>This current Carla build is using the following features and 3rd-party code:</p>"
"<ul>"

// Plugin formats
"<li>LADSPA plugin support</li>"
"<li>DSSI plugin support</li>"
"<li>LV2 plugin support</li>"
"<li>VST2 plugin support using VeSTige header by Javier Serrano Polo</li>"

// Sample kit libraries
#ifdef HAVE_FLUIDSYNTH
"<li>FluidSynth library for SF2/3 support</li>"
#endif
"<li>SFZero module for SFZ support</li>"

// misc libs
"<li>base64 utilities based on code by Ren\u00E9 Nyffenegger</li>"
"<li>liblo library for OSC support</li>"
"<li>rtmempool library by Nedko Arnaudov"
"<li>serd, sord, sratom and lilv libraries for LV2 discovery</li>"
"<li>RtAudio v" RTAUDIO_VERSION " and RtMidi v" RTMIDI_VERSION " for native Audio and MIDI support</li>"

// Internal plugins
"<li>MIDI Sequencer UI code by Perry Nguyen</li>"

// External plugins
#ifdef HAVE_EXTERNAL_PLUGINS
"<li>Nekobi plugin code based on nekobee by Sean Bolton and others</li>"
"<li>VectorJuice and WobbleJuice plugin code by Andre Sklenar</li>"
# ifdef HAVE_ZYN_DEPS
"<li>ZynAddSubFX plugin code by Mark McCurry and Nasca Octavian Paul</li>"
# endif
#endif // HAVE_EXTERNAL_PLUGINS

// end
"</ul>";
}

return retText;
}

const char* const* carla_get_supported_file_extensions()
{
carla_debug("carla_get_supported_file_extensions()");

// NOTE: please keep in sync with CarlaEngine::loadFile!!
static const char* const extensions[] = {
// Base types
"carxp", "carxs",

// plugin files and resources
#ifdef HAVE_FLUIDSYNTH
"sf2", "sf3",
#endif
#ifdef HAVE_ZYN_DEPS
"xmz", "xiz",
#endif
#if defined(CARLA_OS_MAC)
"vst",
#else
"dll",
"so",
#endif

// Audio files
#ifdef HAVE_SNDFILE
"aif", "aifc", "aiff", "au", "bwf", "flac", "htk", "iff", "mat4", "mat5", "oga", "ogg",
"paf", "pvf", "pvf5", "sd2", "sf", "snd", "svx", "vcc", "w64", "wav", "xi",
#endif
#ifdef HAVE_FFMPEG
"3g2", "3gp", "aac", "ac3", "amr", "ape", "mp2", "mp3", "mpc", "wma",
# ifdef HAVE_SNDFILE
// FFmpeg without sndfile
"flac", "oga", "ogg", "w64", "wav",
# endif
#endif

// MIDI files
"mid", "midi",

// SFZ
"sfz",

// terminator
nullptr
};

return extensions;
}

const char* const* carla_get_supported_features()
{
carla_debug("carla_get_supported_features()");

static const char* const features[] = {
#ifdef HAVE_FLUIDSYNTH
"sf2",
#endif
#ifdef HAVE_HYLIA
"link",
#endif
#ifdef HAVE_LIBLO
"osc",
#endif
#if defined(HAVE_LIBMAGIC) || defined(CARLA_OS_WIN)
"bridges",
#endif
#ifdef HAVE_PYQT
"gui",
#endif
nullptr
};

return features;
}

// -------------------------------------------------------------------------------------------------------------------

#include "../CarlaHostCommon.cpp"

// -------------------------------------------------------------------------------------------------------------------

+ 72
- 0
source/backend/utils/Makefile View File

@@ -0,0 +1,72 @@
#!/usr/bin/make -f
# Makefile for carla-plugin #
# ------------------------- #
# Created by falkTX
#

CWD=../..
include ../Makefile.mk

# ----------------------------------------------------------------------------------------------------------------------------

OBJS = \
$(OBJDIR)/CachedPlugins.cpp.o \
$(OBJDIR)/Information.cpp.o \
$(OBJDIR)/PipeClient.cpp.o \
$(OBJDIR)/System.cpp.o \
$(OBJDIR)/Windows.cpp.o

TARGETS = $(BINDIR)/libcarla_utils$(LIB_EXT)

# ----------------------------------------------------------------------------------------------------------------------------

LIBS = $(MODULEDIR)/lilv.a
LIBS += $(MODULEDIR)/water.files.a

LINK_FLAGS += $(LILV_LIBS)
LINK_FLAGS += $(WATER_LIBS)

ifeq ($(HAVE_X11),true)
LINK_FLAGS += $(X11_LIBS)
endif

ifneq ($(HAIKU),true)
LINK_FLAGS += -lpthread
endif

# ----------------------------------------------------------------------------------------------------------------------------

all: $(TARGETS)

# ----------------------------------------------------------------------------------------------------------------------------

clean:
rm -f $(OBJS) $(TARGETS)

debug:
$(MAKE) DEBUG=true

# ----------------------------------------------------------------------------------------------------------------------------

$(BINDIR)/libcarla_utils$(LIB_EXT): $(OBJS) $(LIBS)
-@mkdir -p $(BINDIR)
@echo "Linking libcarla_utils$(LIB_EXT)"
@$(CXX) $(OBJS) $(LIBS_START) $(LIBS) $(LIBS_END) $(LINK_FLAGS) $(LINK_FLAGS) $(SHARED) -o $@

# ----------------------------------------------------------------------------------------------------------------------------

ifeq ($(MACOS),true)
$(OBJDIR)/Windows.cpp.o: Windows.cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $<"
@$(CXX) $< $(BUILD_CXX_FLAGS) -ObjC++ -c -o $@
endif

$(OBJDIR)/%.cpp.o: %.cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $<"
@$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@

-include $(OBJS:%.o=%.d)

# ----------------------------------------------------------------------------------------------------------------------------

+ 169
- 0
source/backend/utils/PipeClient.cpp View File

@@ -0,0 +1,169 @@
/*
* Carla Plugin Host
* Copyright (C) 2011-2018 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

#include "CarlaUtils.h"

#include "CarlaPipeUtils.hpp"

namespace CB = CarlaBackend;

// -------------------------------------------------------------------------------------------------------------------

class ExposedCarlaPipeClient : public CarlaPipeClient
{
public:
ExposedCarlaPipeClient(const CarlaPipeCallbackFunc callbackFunc, void* const callbackPtr) noexcept
: CarlaPipeClient(),
fCallbackFunc(callbackFunc),
fCallbackPtr(callbackPtr),
fLastReadLine(nullptr)
{
CARLA_SAFE_ASSERT(fCallbackFunc != nullptr);
}

~ExposedCarlaPipeClient() override
{
if (fLastReadLine != nullptr)
{
delete[] fLastReadLine;
fLastReadLine = nullptr;
}
}

const char* readlineblock(const uint timeout) noexcept
{
delete[] fLastReadLine;
fLastReadLine = CarlaPipeClient::_readlineblock(timeout);
return fLastReadLine;
}

bool msgReceived(const char* const msg) noexcept override
{
if (fCallbackFunc != nullptr)
{
try {
fCallbackFunc(fCallbackPtr, msg);
} CARLA_SAFE_EXCEPTION("msgReceived");
}

return true;
}

private:
const CarlaPipeCallbackFunc fCallbackFunc;
void* const fCallbackPtr;
const char* fLastReadLine;

CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ExposedCarlaPipeClient)
};

CarlaPipeClientHandle carla_pipe_client_new(const char* argv[], CarlaPipeCallbackFunc callbackFunc, void* callbackPtr)
{
carla_debug("carla_pipe_client_new(%p, %p, %p)", argv, callbackFunc, callbackPtr);

ExposedCarlaPipeClient* const pipe = new ExposedCarlaPipeClient(callbackFunc, callbackPtr);

if (! pipe->initPipeClient(argv))
{
delete pipe;
return nullptr;
}

return pipe;
}

void carla_pipe_client_idle(CarlaPipeClientHandle handle)
{
CARLA_SAFE_ASSERT_RETURN(handle != nullptr,);

((ExposedCarlaPipeClient*)handle)->idlePipe();
}

bool carla_pipe_client_is_running(CarlaPipeClientHandle handle)
{
CARLA_SAFE_ASSERT_RETURN(handle != nullptr, false);

return ((ExposedCarlaPipeClient*)handle)->isPipeRunning();
}

void carla_pipe_client_lock(CarlaPipeClientHandle handle)
{
CARLA_SAFE_ASSERT_RETURN(handle != nullptr,);

return ((ExposedCarlaPipeClient*)handle)->lockPipe();
}

void carla_pipe_client_unlock(CarlaPipeClientHandle handle)
{
CARLA_SAFE_ASSERT_RETURN(handle != nullptr,);

return ((ExposedCarlaPipeClient*)handle)->unlockPipe();
}

const char* carla_pipe_client_readlineblock(CarlaPipeClientHandle handle, uint timeout)
{
CARLA_SAFE_ASSERT_RETURN(handle != nullptr, nullptr);

return ((ExposedCarlaPipeClient*)handle)->readlineblock(timeout);
}

bool carla_pipe_client_write_msg(CarlaPipeClientHandle handle, const char* msg)
{
CARLA_SAFE_ASSERT_RETURN(handle != nullptr, false);

return ((ExposedCarlaPipeClient*)handle)->writeMessage(msg);
}

bool carla_pipe_client_write_and_fix_msg(CarlaPipeClientHandle handle, const char* msg)
{
CARLA_SAFE_ASSERT_RETURN(handle != nullptr, false);

return ((ExposedCarlaPipeClient*)handle)->writeAndFixMessage(msg);
}

bool carla_pipe_client_flush(CarlaPipeClientHandle handle)
{
CARLA_SAFE_ASSERT_RETURN(handle != nullptr, false);

return ((ExposedCarlaPipeClient*)handle)->flushMessages();
}

bool carla_pipe_client_flush_and_unlock(CarlaPipeClientHandle handle)
{
CARLA_SAFE_ASSERT_RETURN(handle != nullptr, false);

ExposedCarlaPipeClient* const pipe = (ExposedCarlaPipeClient*)handle;
const bool ret = pipe->flushMessages();
pipe->unlockPipe();
return ret;
}

void carla_pipe_client_destroy(CarlaPipeClientHandle handle)
{
CARLA_SAFE_ASSERT_RETURN(handle != nullptr,);
carla_debug("carla_pipe_client_destroy(%p)", handle);

ExposedCarlaPipeClient* const pipe = (ExposedCarlaPipeClient*)handle;
pipe->closePipeClient();
delete pipe;
}

// -------------------------------------------------------------------------------------------------------------------

#include "CarlaPipeUtils.cpp"

// -------------------------------------------------------------------------------------------------------------------

+ 41
- 0
source/backend/utils/System.cpp View File

@@ -0,0 +1,41 @@
/*
* Carla Plugin Host
* Copyright (C) 2011-2018 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

#include "CarlaUtils.h"

#include "CarlaThread.hpp"

// -------------------------------------------------------------------------------------------------------------------

void carla_fflush(bool err)
{
std::fflush(err ? stderr : stdout);
}

void carla_fputs(bool err, const char* string)
{
std::fputs(string, err ? stderr : stdout);
}

void carla_set_process_name(const char* name)
{
carla_debug("carla_set_process_name(\"%s\")", name);

CarlaThread::setCurrentThreadName(name);
}

// -------------------------------------------------------------------------------------------------------------------

+ 108
- 0
source/backend/utils/Windows.cpp View File

@@ -0,0 +1,108 @@
/*
* Carla Plugin Host
* Copyright (C) 2011-2018 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

#include "CarlaUtils.h"

#include "CarlaUtils.hpp"

#ifdef CARLA_OS_MAC
# import <Cocoa/Cocoa.h>
#endif

#ifdef HAVE_X11
# include <X11/Xlib.h>
#endif

namespace CB = CarlaBackend;

// -------------------------------------------------------------------------------------------------------------------

void carla_x11_reparent_window(uintptr_t winId1, uintptr_t winId2)
{
CARLA_SAFE_ASSERT_RETURN(winId1 != 0,);
CARLA_SAFE_ASSERT_RETURN(winId2 != 0,);

#ifdef HAVE_X11
if (::Display* const disp = XOpenDisplay(nullptr))
{
XReparentWindow(disp, winId1, winId2, 0, 0);
XMapWindow(disp, winId1);
XCloseDisplay(disp);
}
#endif
}

void carla_x11_move_window(uintptr_t winId, int x, int y)
{
CARLA_SAFE_ASSERT_RETURN(winId != 0,);

#ifdef HAVE_X11
if (::Display* const disp = XOpenDisplay(nullptr))
{
XMoveWindow(disp, winId, x, y);
XCloseDisplay(disp);
}
#else
// unused
return; (void)x; (void)y;
#endif
}

int* carla_x11_get_window_pos(uintptr_t winId)
{
static int pos[2];

if (winId == 0)
{
pos[0] = 0;
pos[1] = 0;
}
#ifdef HAVE_X11
else if (::Display* const disp = XOpenDisplay(nullptr))
{
int x, y;
Window child;
XWindowAttributes xwa;
XTranslateCoordinates(disp, winId, XRootWindow(disp, 0), 0, 0, &x, &y, &child);
XGetWindowAttributes(disp, winId, &xwa);
XCloseDisplay(disp);
pos[0] = x - xwa.x;
pos[1] = y - xwa.y;
}
#endif
else
{
pos[0] = 0;
pos[1] = 0;
}

return pos;
}

int carla_cocoa_get_window(void* nsViewPtr)
{
CARLA_SAFE_ASSERT_RETURN(nsViewPtr != nullptr, 0);

#ifdef CARLA_OS_MAC
NSView* const nsView = (NSView*)nsViewPtr;
return [[nsView window] windowNumber];
#else
return 0;
#endif
}

// -------------------------------------------------------------------------------------------------------------------

+ 4
- 3
source/bridges-plugin/CarlaBridgeSingleLV2.cpp View File

@@ -40,7 +40,7 @@ public:
CarlaEngineSingleLV2(const double sampleRate,
const char* const bundlePath,
const LV2_Feature* const* const features)
: Lv2PluginBaseClass(sampleRate, features),
: Lv2PluginBaseClass<EngineTimeInfo>(sampleRate, features),
fPlugin(nullptr),
fUiName()
{
@@ -93,6 +93,7 @@ public:
CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
CARLA_SAFE_ASSERT_RETURN(fPlugin->isEnabled(),);

fPorts.hasUI = false;
fPorts.usesTime = true;
fPorts.numAudioIns = fPlugin->getAudioInCount();
fPorts.numAudioOuts = fPlugin->getAudioOutCount();
@@ -479,10 +480,10 @@ private:
CARLA_SAFE_ASSERT_RETURN(midiData != nullptr, false);
CARLA_SAFE_ASSERT_RETURN(midiSize > 0, false);

LV2_Atom_Sequence* const seq(fPorts.midiOuts[port]);
LV2_Atom_Sequence* const seq(fPorts.eventsOut[port]);
CARLA_SAFE_ASSERT_RETURN(seq != nullptr, false);

Ports::MidiOutData& mData(fPorts.midiOutData[port]);
Ports::EventsOutData& mData(fPorts.eventsOutData[port]);

if (sizeof(LV2_Atom_Event) + midiSize > mData.capacity - mData.offset)
return false;


+ 3
- 5
source/bridges-plugin/Makefile View File

@@ -68,14 +68,11 @@ LINK_FLAGS += $(X11_LIBS)
# ----------------------------------------------------------------------------------------------------------------------

NATIVE_BUILD_FLAGS = $(NATIVE_PLUGINS_FLAGS) $(LIBLO_FLAGS)
NATIVE_LINK_FLAGS = $(LIBLO_LIBS)
# NATIVE_LINK_FLAGS = $(LIBLO_LIBS)

NATIVE_BUILD_FLAGS += $(FLUIDSYNTH_FLAGS)
NATIVE_LINK_FLAGS += $(FLUIDSYNTH_LIBS)

NATIVE_BUILD_FLAGS += $(LINUXSAMPLER_FLAGS)
NATIVE_LINK_FLAGS += $(LINUXSAMPLER_LIBS)

NATIVE_LINK_FLAGS += $(FFMPEG_LIBS)
NATIVE_LINK_FLAGS += $(MAGIC_LIBS)
NATIVE_LINK_FLAGS += $(SNDFILE_LIBS)
@@ -95,8 +92,9 @@ OBJS_native = \
$(OBJDIR)/CarlaEngine.cpp.o \
$(OBJDIR)/CarlaEngineClient.cpp.o \
$(OBJDIR)/CarlaEngineData.cpp.o \
$(OBJDIR)/CarlaEngineGraph.cpp.o \
$(OBJDIR)/CarlaEngineInternal.cpp.o \
$(OBJDIR)/CarlaEngineOsc.cpp.o \
$(OBJDIR)/CarlaEngineNative.cpp.o \
$(OBJDIR)/CarlaEngineOscSend.cpp.o \
$(OBJDIR)/CarlaEnginePorts.cpp.o \
$(OBJDIR)/CarlaEngineThread.cpp.o \


+ 0
- 4
source/bridges-ui/CarlaBridgeFormat.cpp View File

@@ -351,8 +351,4 @@ CARLA_BRIDGE_UI_END_NAMESPACE

#include "CarlaPipeUtils.cpp"

#if !(defined(DEBUG) || defined(BUILDING_CARLA_FOR_WINDOWS))
# include "water/misc/Time.cpp"
#endif

// ---------------------------------------------------------------------

+ 1
- 0
source/bridges-ui/CarlaBridgeToolkitNative.cpp View File

@@ -226,5 +226,6 @@ CARLA_BRIDGE_UI_END_NAMESPACE

#define CARLA_PLUGIN_UI_CLASS_PREFIX ToolkitNative
#include "CarlaPluginUI.cpp"
#include "CarlaMacUtils.cpp"

// -------------------------------------------------------------------------

+ 64
- 0
source/carla-rest-frontend View File

@@ -0,0 +1,64 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Carla plugin host
# Copyright (C) 2011-2018 Filipe Coelho <falktx@falktx.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of
# the License, or any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# For a full copy of the GNU General Public License see the doc/GPL.txt file.

# ----------------------------------------------------------------------------------------------------------------------
# Imports (Custom Stuff)

from carla_backend_qtweb import CarlaHostQtWeb
from carla_host import (
CarlaApplication,
HostWindow,
setUpSignals,
initHost,
loadHostSettings,
)

# ----------------------------------------------------------------------------------------------------------------------
# Main

if __name__ == '__main__':
# ------------------------------------------------------------------------------------------------------------------
# App initialization

app = CarlaApplication("Carla2-REST")

# ------------------------------------------------------------------------------------------------------------------
# Set-up custom signal handling

setUpSignals()

# ------------------------------------------------------------------------------------------------------------------
# Init host backend

host = initHost("Carla-REST", None, False, False, False, CarlaHostQtWeb)
loadHostSettings(host)

# ------------------------------------------------------------------------------------------------------------------
# Create GUI

gui = HostWindow(host, True)

# ------------------------------------------------------------------------------------------------------------------
# Show GUI

gui.show()

# ------------------------------------------------------------------------------------------------------------------
# App-Loop

app.exit_exec()

+ 0
- 1
source/carla_backend.pro View File

@@ -17,7 +17,6 @@ DEFINES += REAL_BUILD
DEFINES += HAVE_LIBLO
DEFINES += HAVE_LIBMAGIC
DEFINES += HAVE_FLUIDSYNTH
DEFINES += HAVE_LINUXSAMPLER
DEFINES += HAVE_ZYN_DEPS
DEFINES += HAVE_ZYN_UI_DEPS



+ 29
- 3
source/carla_backend.py View File

@@ -1241,6 +1241,7 @@ class CarlaHostMeta(object):
# info about this host object
self.isControl = False
self.isPlugin = False
self.isRemote = False
self.nsmOK = False

# settings
@@ -1571,7 +1572,7 @@ class CarlaHostMeta(object):
def get_midi_program_data(self, pluginId, midiProgramId):
raise NotImplementedError

# Get a plugin's custom data.
# Get a plugin's custom data, using index.
# @param pluginId Plugin
# @param customDataId Custom data index
# @see carla_get_custom_data_count()
@@ -1579,6 +1580,15 @@ class CarlaHostMeta(object):
def get_custom_data(self, pluginId, customDataId):
raise NotImplementedError

# Get a plugin's custom data value, using type and key.
# @param pluginId Plugin
# @param type Custom data type
# @param key Custom data key
# @see carla_get_custom_data_count()
@abstractmethod
def get_custom_data_value(self, pluginId, type_, key):
raise NotImplementedError

# Get a plugin's chunk data.
# @param pluginId Plugin
# @see PLUGIN_OPTION_USE_CHUNKS
@@ -1881,6 +1891,7 @@ class CarlaHostNull(CarlaHostMeta):
CarlaHostMeta.__init__(self)

self.fEngineCallback = None
self.fFileCallback = None
self.fEngineRunning = False

def get_engine_driver_count(self):
@@ -1911,7 +1922,7 @@ class CarlaHostNull(CarlaHostMeta):
return

def is_engine_running(self):
return False
return self.fEngineRunning

def set_engine_about_to_close(self):
return True
@@ -1923,7 +1934,7 @@ class CarlaHostNull(CarlaHostMeta):
return

def set_file_callback(self, func):
return
self.fFileCallback = func

def load_file(self, filename):
return False
@@ -2027,6 +2038,9 @@ class CarlaHostNull(CarlaHostMeta):
def get_custom_data(self, pluginId, customDataId):
return PyCustomData

def get_custom_data_value(self, pluginId, type_, key):
return ""

def get_chunk_data(self, pluginId):
return ""

@@ -2309,6 +2323,9 @@ class CarlaHostDLL(CarlaHostMeta):
self.lib.carla_get_custom_data.argtypes = [c_uint, c_uint32]
self.lib.carla_get_custom_data.restype = POINTER(CustomData)

self.lib.carla_get_custom_data_value.argtypes = [c_uint, c_char_p, c_char_p]
self.lib.carla_get_custom_data_value.restype = c_char_p

self.lib.carla_get_chunk_data.argtypes = [c_uint]
self.lib.carla_get_chunk_data.restype = c_char_p

@@ -2586,6 +2603,9 @@ class CarlaHostDLL(CarlaHostMeta):
def get_custom_data(self, pluginId, customDataId):
return structToDict(self.lib.carla_get_custom_data(pluginId, customDataId).contents)

def get_custom_data_value(self, pluginId, type_, key):
return charPtrToString(self.lib.carla_get_custom_data_value(pluginId, type_.encode("utf-8"), key.encode("utf-8")))

def get_chunk_data(self, pluginId):
return charPtrToString(self.lib.carla_get_chunk_data(pluginId))

@@ -2929,6 +2949,12 @@ class CarlaHostPlugin(CarlaHostMeta):
def get_custom_data(self, pluginId, customDataId):
return self.fPluginsInfo[pluginId].customData[customDataId]

def get_custom_data_value(self, pluginId, type_, key):
for customData in self.fPluginsInfo[pluginId].customData:
if customData['type'] == type_ and customData['key'] == key:
return customData['value']
return ""

def get_chunk_data(self, pluginId):
return ""



+ 568
- 0
source/carla_backend_qtweb.py View File

@@ -0,0 +1,568 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Carla Backend code (Web stuff)
# Copyright (C) 2018 Filipe Coelho <falktx@falktx.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of
# the License, or any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# For a full copy of the GNU General Public License see the doc/GPL.txt file.

# ---------------------------------------------------------------------------------------------------------------------
# Imports (Global)

import requests

# ---------------------------------------------------------------------------------------------------------------------
# Imports (Custom)

from carla_backend_qt import *

import os
from time import sleep

# ---------------------------------------------------------------------------------------------------------------------
# Iterates over the content of a file-like object line-by-line.
# Based on code by Lars Kellogg-Stedman, see https://github.com/requests/requests/issues/2433

def iterate_stream_nonblock(stream, chunk_size=1024):
pending = None

while True:
try:
chunk = os.read(stream.raw.fileno(), chunk_size)
except BlockingIOError:
break
if not chunk:
break

if pending is not None:
chunk = pending + chunk
pending = None

lines = chunk.splitlines()

if lines and lines[-1]:
pending = lines.pop()

for line in lines:
yield line

if not pending:
break

if pending:
yield pending

# ---------------------------------------------------------------------------------------------------------------------

def create_stream(baseurl):
stream = requests.get("{}/stream".format(baseurl), stream=True, timeout=0.1)

if stream.encoding is None:
stream.encoding = 'utf-8'

return stream

# ---------------------------------------------------------------------------------------------------------------------
# Carla Host object for connecting to the REST API backend

class CarlaHostQtWeb(CarlaHostQtNull):
def __init__(self):
CarlaHostQtNull.__init__(self)

self.baseurl = "http://localhost:2228"
self.stream = create_stream(self.baseurl)

self.isRemote = True

def get_engine_driver_count(self):
# FIXME
return int(requests.get("{}/get_engine_driver_count".format(self.baseurl)).text) - 1

def get_engine_driver_name(self, index):
return requests.get("{}/get_engine_driver_name".format(self.baseurl), params={
'index': index,
}).text

def get_engine_driver_device_names(self, index):
return requests.get("{}/get_engine_driver_device_names".format(self.baseurl), params={
'index': index,
}).text.split("\n")

def get_engine_driver_device_info(self, index, name):
return requests.get("{}/get_engine_driver_device_info".format(self.baseurl), params={
'index': index,
'name': name,
}).json()

def engine_init(self, driverName, clientName):
return bool(int(requests.get("{}/engine_init".format(self.baseurl), params={
'driverName': driverName,
'clientName': clientName,
}).text))

def engine_close(self):
return bool(int(requests.get("{}/engine_close".format(self.baseurl)).text))

def engine_idle(self):
closed = False
stream = self.stream

for line in iterate_stream_nonblock(stream):
line = line.decode('utf-8', errors='ignore')

if line.startswith("Carla: "):
if self.fEngineCallback is None:
continue

# split values from line
action, pluginId, value1, value2, value3, valueStr = line[7:].split(" ",5)

# convert to proper types
action = int(action)
pluginId = int(pluginId)
value1 = int(value1)
value2 = int(value2)
value3 = float(value3)

# pass to callback
self.fEngineCallback(None, action, pluginId, value1, value2, value3, valueStr)

elif line == "Connection: close":
if not closed:
self.stream = create_stream(self.baseurl)
closed = True

if closed:
stream.close()

def is_engine_running(self):
try:
return bool(int(requests.get("{}/is_engine_running".format(self.baseurl)).text))
except requests.exceptions.ConnectionError:
if self.fEngineCallback is None:
self.fEngineCallback(None, ENGINE_CALLBACK_QUIT, 0, 0, 0, 0.0, "")

def set_engine_about_to_close(self):
return bool(int(requests.get("{}/set_engine_about_to_close".format(self.baseurl)).text))

def set_engine_option(self, option, value, valueStr):
requests.get("{}/set_engine_option".format(self.baseurl), params={
'option': option,
'value': value,
'valueStr': valueStr,
})

def load_file(self, filename):
return bool(int(requests.get("{}/load_file".format(self.baseurl), params={
'filename': filename,
}).text))

def load_project(self, filename):
return bool(int(requests.get("{}/load_project".format(self.baseurl), params={
'filename': filename,
}).text))

def save_project(self, filename):
return bool(int(requests.get("{}/save_project".format(self.baseurl), params={
'filename': filename,
}).text))

def patchbay_connect(self, groupIdA, portIdA, groupIdB, portIdB):
return bool(int(requests.get("{}/patchbay_connect".format(self.baseurl), params={
'groupIdA': groupIdA,
'portIdA': portIdA,
'groupIdB': groupIdB,
'portIdB': portIdB,
}).text))

def patchbay_disconnect(self, connectionId):
return bool(int(requests.get("{}/patchbay_disconnect".format(self.baseurl), params={
'connectionId': connectionId,
}).text))

def patchbay_refresh(self, external):
return bool(int(requests.get("{}/patchbay_refresh".format(self.baseurl), params={
'external': external,
}).text))

def transport_play(self):
requests.get("{}/transport_play".format(self.baseurl))

def transport_pause(self):
requests.get("{}/transport_pause".format(self.baseurl))

def transport_bpm(self, bpm):
requests.get("{}/transport_bpm".format(self.baseurl), params={
'bpm': bpm,
})

def transport_relocate(self, frame):
requests.get("{}/transport_relocate".format(self.baseurl), params={
'frame': frame,
})

def get_current_transport_frame(self):
return int(requests.get("{}/get_current_transport_frame".format(self.baseurl)).text)

def get_transport_info(self):
return requests.get("{}/get_transport_info".format(self.baseurl)).json()

def get_current_plugin_count(self):
return int(requests.get("{}/get_current_plugin_count".format(self.baseurl)).text)

def get_max_plugin_number(self):
return int(requests.get("{}/get_max_plugin_number".format(self.baseurl)).text)

def add_plugin(self, btype, ptype, filename, name, label, uniqueId, extraPtr, options):
return bool(int(requests.get("{}/add_plugin".format(self.baseurl), params={
'btype': btype,
'ptype': ptype,
'filename': filename,
'name': name,
'label': label,
'uniqueId': uniqueId,
'options': options,
}).text))

def remove_plugin(self, pluginId):
return bool(int(requests.get("{}/remove_plugin".format(self.baseurl), params={
'filename': pluginId,
}).text))

def remove_all_plugins(self):
return bool(int(requests.get("{}/remove_all_plugins".format(self.baseurl)).text))

def rename_plugin(self, pluginId, newName):
return requests.get("{}/rename_plugin".format(self.baseurl), params={
'pluginId': pluginId,
'newName': newName,
}).text

def clone_plugin(self, pluginId):
return bool(int(requests.get("{}/clone_plugin".format(self.baseurl), params={
'pluginId': pluginId,
}).text))

def replace_plugin(self, pluginId):
return bool(int(requests.get("{}/replace_plugin".format(self.baseurl), params={
'pluginId': pluginId,
}).text))

def switch_plugins(self, pluginIdA, pluginIdB):
return bool(int(requests.get("{}/switch_plugins".format(self.baseurl), params={
'pluginIdA': pluginIdA,
'pluginIdB': pluginIdB,
}).text))

def load_plugin_state(self, pluginId, filename):
return bool(int(requests.get("{}/load_plugin_state".format(self.baseurl), params={
'pluginId': pluginId,
'filename': filename,
}).text))

def save_plugin_state(self, pluginId, filename):
return bool(int(requests.get("{}/save_plugin_state".format(self.baseurl), params={
'pluginId': pluginId,
'filename': filename,
}).text))

def export_plugin_lv2(self, pluginId, lv2path):
return bool(int(requests.get("{}/export_plugin_lv2".format(self.baseurl), params={
'pluginId': pluginId,
'lv2path': lv2path,
}).text))

def get_plugin_info(self, pluginId):
return requests.get("{}/get_plugin_info".format(self.baseurl), params={
'pluginId': pluginId,
}).json()

def get_audio_port_count_info(self, pluginId):
return requests.get("{}/get_audio_port_count_info".format(self.baseurl), params={
'pluginId': pluginId,
}).json()

def get_midi_port_count_info(self, pluginId):
return requests.get("{}/get_midi_port_count_info".format(self.baseurl), params={
'pluginId': pluginId,
}).json()

def get_parameter_count_info(self, pluginId):
return requests.get("{}/get_parameter_count_info".format(self.baseurl), params={
'pluginId': pluginId,
}).json()

def get_parameter_info(self, pluginId, parameterId):
return requests.get("{}/get_parameter_info".format(self.baseurl), params={
'pluginId': pluginId,
'parameterId': parameterId,
}).json()

def get_parameter_scalepoint_info(self, pluginId, parameterId, scalePointId):
return requests.get("{}/get_parameter_scalepoint_info".format(self.baseurl), params={
'pluginId': pluginId,
'parameterId': parameterId,
'scalePointId': scalePointId,
}).json()

def get_parameter_data(self, pluginId, parameterId):
return requests.get("{}/get_parameter_data".format(self.baseurl), params={
'pluginId': pluginId,
'parameterId': parameterId,
}).json()

def get_parameter_ranges(self, pluginId, parameterId):
return requests.get("{}/get_parameter_ranges".format(self.baseurl), params={
'pluginId': pluginId,
'parameterId': parameterId,
}).json()

def get_midi_program_data(self, pluginId, midiProgramId):
return requests.get("{}/get_midi_program_data".format(self.baseurl), params={
'pluginId': pluginId,
'midiProgramId': midiProgramId,
}).json()

def get_custom_data(self, pluginId, customDataId):
return requests.get("{}/get_custom_data".format(self.baseurl), params={
'pluginId': pluginId,
'customDataId': customDataId,
}).json()

def get_custom_data_value(self, pluginId, type_, key):
return requests.get("{}/get_custom_data_value".format(self.baseurl), params={
'pluginId': pluginId,
'type_': type_,
'key': key,
}).text

def get_chunk_data(self, pluginId):
return requests.get("{}/get_chunk_data".format(self.baseurl), params={
'pluginId': pluginId,
}).text

def get_parameter_count(self, pluginId):
return int(requests.get("{}/get_parameter_count".format(self.baseurl), params={
'pluginId': pluginId,
}).text)

def get_program_count(self, pluginId):
return int(requests.get("{}/get_program_count".format(self.baseurl), params={
'pluginId': pluginId,
}).text)

def get_midi_program_count(self, pluginId):
return int(requests.get("{}/get_midi_program_count".format(self.baseurl), params={
'pluginId': pluginId,
}).text)

def get_custom_data_count(self, pluginId):
return int(requests.get("{}/get_custom_data_count".format(self.baseurl), params={
'pluginId': pluginId,
}).text)

def get_parameter_text(self, pluginId, parameterId):
return requests.get("{}/get_parameter_text".format(self.baseurl), params={
'pluginId': pluginId,
'parameterId': parameterId,
}).text

def get_program_name(self, pluginId, programId):
return requests.get("{}/get_program_name".format(self.baseurl), params={
'pluginId': pluginId,
'programId': programId,
}).text

def get_midi_program_name(self, pluginId, midiProgramId):
return requests.get("{}/get_midi_program_name".format(self.baseurl), params={
'pluginId': pluginId,
'midiProgramId': midiProgramId,
}).text

def get_real_plugin_name(self, pluginId):
return requests.get("{}/get_real_plugin_name".format(self.baseurl), params={
'pluginId': pluginId,
}).text

def get_current_program_index(self, pluginId):
return int(requests.get("{}/get_custom_data_count".format(self.baseurl), params={
'pluginId': pluginId,
}).text)

def get_current_midi_program_index(self, pluginId):
return int(requests.get("{}/get_custom_data_count".format(self.baseurl), params={
'pluginId': pluginId,
}).text)

def get_default_parameter_value(self, pluginId, parameterId):
return float(requests.get("{}/get_default_parameter_value".format(self.baseurl), params={
'pluginId': pluginId,
'parameterId': parameterId,
}).text)

def get_current_parameter_value(self, pluginId, parameterId):
return float(requests.get("{}/get_current_parameter_value".format(self.baseurl), params={
'pluginId': pluginId,
'parameterId': parameterId,
}).text)

def get_internal_parameter_value(self, pluginId, parameterId):
return float(requests.get("{}/get_internal_parameter_value".format(self.baseurl), params={
'pluginId': pluginId,
'parameterId': parameterId,
}).text)

def get_input_peak_value(self, pluginId, isLeft):
return float(requests.get("{}/get_input_peak_value".format(self.baseurl), params={
'pluginId': pluginId,
'isLeft': isLeft,
}).text)

def get_output_peak_value(self, pluginId, isLeft):
return float(requests.get("{}/get_output_peak_value".format(self.baseurl), params={
'pluginId': pluginId,
'isLeft': isLeft,
}).text)

def set_option(self, pluginId, option, yesNo):
requests.get("{}/set_option".format(self.baseurl), params={
'pluginId': pluginId,
'option': option,
'yesNo': yesNo,
})

def set_active(self, pluginId, onOff):
requests.get("{}/set_active".format(self.baseurl), params={
'pluginId': pluginId,
'onOff': onOff,
})

def set_drywet(self, pluginId, value):
requests.get("{}/set_drywet".format(self.baseurl), params={
'pluginId': pluginId,
'value': value,
})

def set_volume(self, pluginId, value):
requests.get("{}/set_volume".format(self.baseurl), params={
'pluginId': pluginId,
'value': value,
})

def set_balance_left(self, pluginId, value):
requests.get("{}/set_balance_left".format(self.baseurl), params={
'pluginId': pluginId,
'value': value,
})

def set_balance_right(self, pluginId, value):
requests.get("{}/set_balance_right".format(self.baseurl), params={
'pluginId': pluginId,
'value': value,
})

def set_panning(self, pluginId, value):
requests.get("{}/set_panning".format(self.baseurl), params={
'pluginId': pluginId,
'value': value,
})

def set_ctrl_channel(self, pluginId, channel):
requests.get("{}/set_ctrl_channel".format(self.baseurl), params={
'pluginId': pluginId,
'channel': channel,
})

def set_parameter_value(self, pluginId, parameterId, value):
requests.get("{}/set_parameter_value".format(self.baseurl), params={
'pluginId': pluginId,
'parameterId': parameterId,
'value': value,
})

def set_parameter_midi_channel(self, pluginId, parameterId, channel):
requests.get("{}/set_parameter_midi_channel".format(self.baseurl), params={
'pluginId': pluginId,
'parameterId': parameterId,
'channel': channel,
})

def set_parameter_midi_cc(self, pluginId, parameterId, cc):
requests.get("{}/set_parameter_midi_cc".format(self.baseurl), params={
'pluginId': pluginId,
'parameterId': parameterId,
'cc': cc,
})

def set_program(self, pluginId, programId):
requests.get("{}/set_program".format(self.baseurl), params={
'pluginId': pluginId,
})

def set_midi_program(self, pluginId, midiProgramId):
requests.get("{}/set_midi_program".format(self.baseurl), params={
'pluginId': pluginId,
'midiProgramId': midiProgramId,
})

def set_custom_data(self, pluginId, type_, key, value):
requests.get("{}/set_custom_data".format(self.baseurl), params={
'pluginId': pluginId,
'type': type_,
'key': key,
'value': value,
})

def set_chunk_data(self, pluginId, chunkData):
requests.get("{}/set_chunk_data".format(self.baseurl), params={
'pluginId': pluginId,
'chunkData': chunkData,
})

def prepare_for_save(self, pluginId):
requests.get("{}/prepare_for_save".format(self.baseurl), params={
'pluginId': pluginId,
})

def reset_parameters(self, pluginId):
requests.get("{}/reset_parameters".format(self.baseurl), params={
'pluginId': pluginId,
})

def randomize_parameters(self, pluginId):
requests.get("{}/randomize_parameters".format(self.baseurl), params={
'pluginId': pluginId,
})

def send_midi_note(self, pluginId, channel, note, velocity):
requests.get("{}/send_midi_note".format(self.baseurl), params={
'pluginId': pluginId,
'channel': channel,
'note': note,
'velocity': velocity,
})

def get_buffer_size(self):
return int(requests.get("{}/get_buffer_size".format(self.baseurl)).text)

def get_sample_rate(self):
return float(requests.get("{}/get_sample_rate".format(self.baseurl)).text)

def get_last_error(self):
return requests.get("{}/get_last_error".format(self.baseurl)).text

def get_host_osc_url_tcp(self):
return requests.get("{}/get_host_osc_url_tcp".format(self.baseurl)).text

def get_host_osc_url_udp(self):
return requests.get("{}/get_host_osc_url_udp".format(self.baseurl)).text

+ 215
- 194
source/carla_database.py View File

@@ -28,11 +28,11 @@ from copy import deepcopy
from subprocess import Popen, PIPE

if config_UseQt5:
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QThread, QSettings
from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QTableWidgetItem
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QEventLoop, QThread, QSettings
from PyQt5.QtWidgets import QApplication, QDialog, QDialogButtonBox, QTableWidgetItem
else:
from PyQt4.QtCore import pyqtSignal, pyqtSlot, Qt, QThread, QSettings
from PyQt4.QtGui import QDialog, QDialogButtonBox, QTableWidgetItem
from PyQt4.QtCore import pyqtSignal, pyqtSlot, Qt, QEventLoop, QThread, QSettings
from PyQt4.QtGui import QApplication, QDialog, QDialogButtonBox, QTableWidgetItem

# ----------------------------------------------------------------------------------------------------------------------
# Imports (Custom)
@@ -117,8 +117,6 @@ def findFilenames(filePath, stype):

if stype == "sf2":
extensions = (".sf2",".sf3",)
elif stype == "sfz":
extensions = (".sfz",)
else:
return []

@@ -131,10 +129,11 @@ def findFilenames(filePath, stype):
# ----------------------------------------------------------------------------------------------------------------------
# Plugin Query

PLUGIN_QUERY_API_VERSION = 7
PLUGIN_QUERY_API_VERSION = 8

PyPluginInfo = {
'API': PLUGIN_QUERY_API_VERSION,
'valid': False,
'build': BINARY_NONE,
'type': PLUGIN_NONE,
'hints': 0x0,
@@ -316,8 +315,6 @@ def killDiscovery():
gDiscoveryProcess.kill()

def checkPluginCached(desc, ptype):
plugins = []

pinfo = deepcopy(PyPluginInfo)
pinfo['build'] = BINARY_NATIVE
pinfo['type'] = ptype
@@ -335,9 +332,7 @@ def checkPluginCached(desc, ptype):
pinfo['parameters.ins'] = desc['parameterIns']
pinfo['parameters.outs'] = desc['parameterOuts']

plugins.append(pinfo)

return plugins
return pinfo

def checkPluginLADSPA(filename, tool, wineSettings=None):
return runCarlaDiscovery(PLUGIN_LADSPA, "LADSPA", filename, tool, wineSettings)
@@ -407,13 +402,6 @@ class SearchPluginsThread(QThread):
self.fLastCheckValue = 0
self.fSomethingChanged = False

self.fLadspaPlugins = []
self.fDssiPlugins = []
self.fLv2Plugins = []
self.fVst2Plugins = []
self.fAuPlugins = []
self.fKitPlugins = []

# -------------------------------------------------------------

def hasSomethingChanged(self):
@@ -438,7 +426,7 @@ class SearchPluginsThread(QThread):
self.fContinueChecking = False

def run(self):
settingsDB = QSettings("falkTX", "CarlaPlugins3")
settingsDB = QSettings("falkTX", "CarlaPlugins4")

self.fContinueChecking = True
self.fCurCount = 0
@@ -466,20 +454,24 @@ class SearchPluginsThread(QThread):
if self.fCheckWin64:
self.fCurCount += pluginCount

# Special case for LV2, only "search" for native plugins
# Special case for cached plugins, only "search" for native plugins (does not need tool)
if self.fCheckLV2:
if self.fCheckNative:
self.fCurCount += 1
else:
self.fCheckLV2 = False

if self.fCheckSFZ:
if self.fCheckNative:
self.fCurCount += 1
else:
self.fCheckSFZ = False

# Special case for Sound Kits, only search native
if self.fCheckNative and self.fToolNative:
if self.fCheckSF2: self.fCurCount += 1
if self.fCheckSFZ: self.fCurCount += 1
else:
self.fCheckSF2 = False
self.fCheckSFZ = False

if self.fCurCount == 0:
return
@@ -515,28 +507,28 @@ class SearchPluginsThread(QThread):
rdfPadValue = self.fCurPercentValue * checkValue

if self.fCheckNative:
self._checkLADSPA(OS, self.fToolNative)
settingsDB.setValue("Plugins/LADSPA_native", self.fLadspaPlugins)
plugins = self._checkLADSPA(OS, self.fToolNative)
settingsDB.setValue("Plugins/LADSPA_native", plugins)
if not self.fContinueChecking: return

if self.fCheckPosix32:
self._checkLADSPA(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix32"))
settingsDB.setValue("Plugins/LADSPA_posix32", self.fLadspaPlugins)
plugins = self._checkLADSPA(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix32"))
settingsDB.setValue("Plugins/LADSPA_posix32", plugins)
if not self.fContinueChecking: return

if self.fCheckPosix64:
self._checkLADSPA(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix64"))
settingsDB.setValue("Plugins/LADSPA_posix64", self.fLadspaPlugins)
plugins = self._checkLADSPA(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix64"))
settingsDB.setValue("Plugins/LADSPA_posix64", plugins)
if not self.fContinueChecking: return

if self.fCheckWin32:
self._checkLADSPA("WINDOWS", os.path.join(self.fPathBinaries, "carla-discovery-win32.exe"), not WINDOWS)
settingsDB.setValue("Plugins/LADSPA_win32", self.fLadspaPlugins)
plugins = self._checkLADSPA("WINDOWS", os.path.join(self.fPathBinaries, "carla-discovery-win32.exe"), not WINDOWS)
settingsDB.setValue("Plugins/LADSPA_win32", plugins)
if not self.fContinueChecking: return

if self.fCheckWin64:
self._checkLADSPA("WINDOWS", os.path.join(self.fPathBinaries, "carla-discovery-win64.exe"), not WINDOWS)
settingsDB.setValue("Plugins/LADSPA_win64", self.fLadspaPlugins)
plugins = self._checkLADSPA("WINDOWS", os.path.join(self.fPathBinaries, "carla-discovery-win64.exe"), not WINDOWS)
settingsDB.setValue("Plugins/LADSPA_win64", plugins)

settingsDB.sync()
if not self.fContinueChecking: return
@@ -560,62 +552,63 @@ class SearchPluginsThread(QThread):

if self.fCheckDSSI:
if self.fCheckNative:
self._checkDSSI(OS, self.fToolNative)
settingsDB.setValue("Plugins/DSSI_native", self.fDssiPlugins)
plugins = self._checkDSSI(OS, self.fToolNative)
settingsDB.setValue("Plugins/DSSI_native", plugins)
if not self.fContinueChecking: return

if self.fCheckPosix32:
self._checkDSSI(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix32"))
settingsDB.setValue("Plugins/DSSI_posix32", self.fDssiPlugins)
plugins = self._checkDSSI(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix32"))
settingsDB.setValue("Plugins/DSSI_posix32", plugins)
if not self.fContinueChecking: return

if self.fCheckPosix64:
self._checkDSSI(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix64"))
settingsDB.setValue("Plugins/DSSI_posix64", self.fDssiPlugins)
plugins = self._checkDSSI(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix64"))
settingsDB.setValue("Plugins/DSSI_posix64", plugins)
if not self.fContinueChecking: return

if self.fCheckWin32:
self._checkDSSI("WINDOWS", os.path.join(self.fPathBinaries, "carla-discovery-win32.exe"), not WINDOWS)
settingsDB.setValue("Plugins/DSSI_win32", self.fDssiPlugins)
plugins = self._checkDSSI("WINDOWS", os.path.join(self.fPathBinaries, "carla-discovery-win32.exe"), not WINDOWS)
settingsDB.setValue("Plugins/DSSI_win32", plugins)
if not self.fContinueChecking: return

if self.fCheckWin64:
self._checkDSSI("WINDOWS", os.path.join(self.fPathBinaries, "carla-discovery-win64.exe"), not WINDOWS)
settingsDB.setValue("Plugins/DSSI_win64", self.fDssiPlugins)
plugins = self._checkDSSI("WINDOWS", os.path.join(self.fPathBinaries, "carla-discovery-win64.exe"), not WINDOWS)
settingsDB.setValue("Plugins/DSSI_win64", plugins)

settingsDB.sync()
if not self.fContinueChecking: return

if self.fCheckLV2:
self._checkLv2Cached()
settingsDB.setValue("Plugins/LV2", self.fLv2Plugins)
plugins = self._checkLv2Cached()
settingsDB.setValue("Plugins/LV2", plugins)
settingsDB.sync()
if not self.fContinueChecking: return

if self.fCheckVST2:
if self.fCheckNative:
self._checkVST2(OS, self.fToolNative)
settingsDB.setValue("Plugins/VST2_native", self.fVstPlugins)
plugins = self._checkVST2(OS, self.fToolNative)
settingsDB.setValue("Plugins/VST2_native", plugins)
if not self.fContinueChecking: return

if self.fCheckPosix32:
self._checkVST2(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix32"))
settingsDB.setValue("Plugins/VST2_posix32", self.fVstPlugins)
plugins = self._checkVST2(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix32"))
settingsDB.setValue("Plugins/VST2_posix32", plugins)
if not self.fContinueChecking: return

if self.fCheckPosix64:
self._checkVST2(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix64"))
settingsDB.setValue("Plugins/VST2_posix64", self.fVstPlugins)
plugins = self._checkVST2(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix64"))
settingsDB.setValue("Plugins/VST2_posix64", plugins)
if not self.fContinueChecking: return

if self.fCheckWin32:
self._checkVST2("WINDOWS", os.path.join(self.fPathBinaries, "carla-discovery-win32.exe"), not WINDOWS)
settingsDB.setValue("Plugins/VST2_win32", self.fVstPlugins)
plugins = self._checkVST2("WINDOWS", os.path.join(self.fPathBinaries, "carla-discovery-win32.exe"), not WINDOWS)
settingsDB.setValue("Plugins/VST2_win32", plugins)
if not self.fContinueChecking: return

if self.fCheckWin64:
self._checkVST2("WINDOWS", os.path.join(self.fPathBinaries, "carla-discovery-win64.exe"), not WINDOWS)
settingsDB.setValue("Plugins/VST2_win64", self.fVstPlugins)
plugins = self._checkVST2("WINDOWS", os.path.join(self.fPathBinaries, "carla-discovery-win64.exe"), not WINDOWS)
settingsDB.setValue("Plugins/VST2_win64", plugins)
if not self.fContinueChecking: return

settingsDB.sync()
if not self.fContinueChecking: return
@@ -625,23 +618,19 @@ class SearchPluginsThread(QThread):
SF2_PATH = toList(settings.value(CARLA_KEY_PATHS_SF2, CARLA_DEFAULT_SF2_PATH))
del settings

self._checkKIT(SF2_PATH, "sf2")
settingsDB.setValue("Plugins/SF2", self.fKitPlugins)
kits = self._checkKIT(SF2_PATH, "sf2")
settingsDB.setValue("Plugins/SF2", kits)
settingsDB.sync()
if not self.fContinueChecking: return

if self.fCheckSFZ:
settings = QSettings("falkTX", "Carla2")
SFZ_PATH = toList(settings.value(CARLA_KEY_PATHS_SFZ, CARLA_DEFAULT_SFZ_PATH))
del settings

self._checkKIT(SFZ_PATH, "sfz")
settingsDB.setValue("Plugins/SFZ", self.fKitPlugins)

settingsDB.sync()
kits = self._checkSfzCached()
settingsDB.setValue("Plugins/SFZ", kits)
settingsDB.sync()

def _checkLADSPA(self, OS, tool, isWine=False):
ladspaBinaries = []
self.fLadspaPlugins = []
ladspaPlugins = []

self._pluginLook(self.fLastCheckValue, "LADSPA plugins...")

@@ -657,7 +646,8 @@ class SearchPluginsThread(QThread):

ladspaBinaries.sort()

if not self.fContinueChecking: return
if not self.fContinueChecking:
return ladspaPlugins

for i in range(len(ladspaBinaries)):
ladspa = ladspaBinaries[i]
@@ -666,15 +656,17 @@ class SearchPluginsThread(QThread):

plugins = checkPluginLADSPA(ladspa, tool, self.fWineSettings if isWine else None)
if plugins:
self.fLadspaPlugins.append(plugins)
ladspaPlugins.append(plugins)

if not self.fContinueChecking: break
if not self.fContinueChecking:
break

self.fLastCheckValue += self.fCurPercentValue
return ladspaPlugins

def _checkDSSI(self, OS, tool, isWine=False):
dssiBinaries = []
self.fDssiPlugins = []
dssiPlugins = []

self._pluginLook(self.fLastCheckValue, "DSSI plugins...")

@@ -690,7 +682,8 @@ class SearchPluginsThread(QThread):

dssiBinaries.sort()

if not self.fContinueChecking: return
if not self.fContinueChecking:
return dssiPlugins

for i in range(len(dssiBinaries)):
dssi = dssiBinaries[i]
@@ -699,15 +692,17 @@ class SearchPluginsThread(QThread):

plugins = checkPluginDSSI(dssi, tool, self.fWineSettings if isWine else None)
if plugins:
self.fDssiPlugins.append(plugins)
dssiPlugins.append(plugins)

if not self.fContinueChecking: break
if not self.fContinueChecking:
break

self.fLastCheckValue += self.fCurPercentValue
return dssiPlugins

def _checkVST2(self, OS, tool, isWine=False):
vst2Binaries = []
self.fVstPlugins = []
vstPlugins = []

if MACOS and not isWine:
self._pluginLook(self.fLastCheckValue, "VST2 bundles...")
@@ -729,7 +724,8 @@ class SearchPluginsThread(QThread):

vst2Binaries.sort()

if not self.fContinueChecking: return
if not self.fContinueChecking:
return vstPlugins

for i in range(len(vst2Binaries)):
vst2 = vst2Binaries[i]
@@ -738,15 +734,17 @@ class SearchPluginsThread(QThread):

plugins = checkPluginVST2(vst2, tool, self.fWineSettings if isWine else None)
if plugins:
self.fVstPlugins.append(plugins)
vstPlugins.append(plugins)

if not self.fContinueChecking: break
if not self.fContinueChecking:
break

self.fLastCheckValue += self.fCurPercentValue
return vstPlugins

def _checkKIT(self, kitPATH, kitExtension):
kitFiles = []
self.fKitPlugins = []
kitPlugins = []

for iPATH in kitPATH:
files = findFilenames(iPATH, kitExtension)
@@ -756,7 +754,8 @@ class SearchPluginsThread(QThread):

kitFiles.sort()

if not self.fContinueChecking: return
if not self.fContinueChecking:
return kitPlugins

for i in range(len(kitFiles)):
kit = kitFiles[i]
@@ -765,31 +764,30 @@ class SearchPluginsThread(QThread):

if kitExtension == "sf2":
plugins = checkFileSF2(kit, self.fToolNative)
elif kitExtension == "sfz":
plugins = checkFileSFZ(kit, self.fToolNative)
else:
plugins = None

if plugins:
self.fKitPlugins.append(plugins)
kitPlugins.append(plugins)

if not self.fContinueChecking: break
if not self.fContinueChecking:
break

self.fLastCheckValue += self.fCurPercentValue
return kitPlugins

def _checkLv2Cached(self):
settings = QSettings("falkTX", "Carla2")
PLUG_PATH = splitter.join(toList(settings.value(CARLA_KEY_PATHS_LV2, CARLA_DEFAULT_LV2_PATH)))
del settings

self.fLv2Plugins = []
PLUG_LIST = self.fLv2Plugins

lv2Plugins = []
self._pluginLook(self.fLastCheckValue, "LV2 plugins...")

count = gCarla.utils.get_cached_plugin_count(PLUGIN_LV2, PLUG_PATH)

if not self.fContinueChecking: return
if not self.fContinueChecking:
return lv2Plugins

for i in range(count):
descInfo = gCarla.utils.get_cached_plugin_info(PLUGIN_LV2, i)
@@ -797,14 +795,46 @@ class SearchPluginsThread(QThread):
percent = ( float(i) / count ) * self.fCurPercentValue
self._pluginLook(self.fLastCheckValue + percent, descInfo['label'])

plugins = checkPluginCached(descInfo, PLUGIN_LV2)
if not descInfo['valid']:
continue

lv2Plugins.append(checkPluginCached(descInfo, PLUGIN_LV2))

if plugins:
PLUG_LIST.append(plugins)
if not self.fContinueChecking:
break

self.fLastCheckValue += self.fCurPercentValue
return lv2Plugins

def _checkSfzCached(self):
settings = QSettings("falkTX", "Carla2")
PLUG_PATH = splitter.join(toList(settings.value(CARLA_KEY_PATHS_SFZ, CARLA_DEFAULT_SFZ_PATH)))
del settings

sfzKits = []
self._pluginLook(self.fLastCheckValue, "SFZ kits...")

count = gCarla.utils.get_cached_plugin_count(PLUGIN_SFZ, PLUG_PATH)

if not self.fContinueChecking:
return sfzKits

for i in range(count):
descInfo = gCarla.utils.get_cached_plugin_info(PLUGIN_SFZ, i)

percent = ( float(i) / count ) * self.fCurPercentValue
self._pluginLook(self.fLastCheckValue + percent, descInfo['label'])

if not descInfo['valid']:
continue

if not self.fContinueChecking: break
sfzKits.append(checkPluginCached(descInfo, PLUGIN_SFZ))

if not self.fContinueChecking:
break

self.fLastCheckValue += self.fCurPercentValue
return sfzKits

def _pluginLook(self, percent, plugin):
self.pluginLook.emit(percent, plugin)
@@ -1388,9 +1418,7 @@ class PluginDatabaseW(QDialog):

rowCount = self.ui.tableWidget.rowCount()

for i in range(rowCount):
self.ui.tableWidget.showRow(i)

for i in range(self.fLastTableIndex):
plugin = self.ui.tableWidget.item(i, 0).data(Qt.UserRole)
aIns = plugin['audio.ins']
aOuts = plugin['audio.outs']
@@ -1449,6 +1477,8 @@ class PluginDatabaseW(QDialog):
text in self.ui.tableWidget.item(i, 3).text().lower() or
text in self.ui.tableWidget.item(i, 13).text().lower())):
self.ui.tableWidget.hideRow(i)
else:
self.ui.tableWidget.showRow(i)

# --------------------------------------------------------------------------------------------------------

@@ -1492,7 +1522,6 @@ class PluginDatabaseW(QDialog):

bridgeText = self.tr("Yes (%s)" % typeText)

self.ui.tableWidget.insertRow(index)
self.ui.tableWidget.setItem(index, 0, QTableWidgetItem(str(plugin['name'])))
self.ui.tableWidget.setItem(index, 1, QTableWidgetItem(str(plugin['label'])))
self.ui.tableWidget.setItem(index, 2, QTableWidgetItem(str(plugin['maker'])))
@@ -1512,89 +1541,80 @@ class PluginDatabaseW(QDialog):

# --------------------------------------------------------------------------------------------------------

def _reAddPlugins(self):
settingsDB = QSettings("falkTX", "CarlaPlugins3")

for x in range(self.ui.tableWidget.rowCount()):
self.ui.tableWidget.removeRow(0)

self.fLastTableIndex = 0
self.ui.tableWidget.setSortingEnabled(False)

internalCount = 0
ladspaCount = 0
dssiCount = 0
lv2Count = 0
vstCount = 0
kitCount = 0
def _reAddInternalHelper(self, settingsDB, ptype, path):
if ptype == PLUGIN_INTERNAL:
ptypeStr = "Internal"
ptypeStrTr = self.tr("Internal")
elif ptype == PLUGIN_LV2:
ptypeStr = "LV2"
ptypeStrTr = ptypeStr
elif ptype == PLUGIN_SFZ:
ptypeStr = "SFZ"
ptypeStrTr = ptypeStr
else:
return 0

settings = QSettings("falkTX", "Carla2")
LV2_PATH = splitter.join(toList(settings.value(CARLA_KEY_PATHS_LV2, CARLA_DEFAULT_LV2_PATH)))
del settings
#qApp = QApplication.instance()

# ----------------------------------------------------------------------------------------------------
# Cached plugins (Internal)
plugins = toList(settingsDB.value("Plugins/" + ptypeStr, []))
pluginCount = settingsDB.value("PluginCount/" + ptypeStr, 0, type=int)

internalPlugins = toList(settingsDB.value("Plugins/Internal", []))
pluginCountNew = gCarla.utils.get_cached_plugin_count(ptype, path)

for plugins in internalPlugins:
internalCount += len(plugins)
if pluginCountNew != pluginCount or (len(plugins) > 0 and plugins[0]['API'] != PLUGIN_QUERY_API_VERSION):
plugins = []
pluginCount = pluginCountNew

internalCountNew = gCarla.utils.get_cached_plugin_count(PLUGIN_INTERNAL, "")
QApplication.processEvents(QEventLoop.ExcludeUserInputEvents, 50)

if internalCountNew != internalCount or (len(internalPlugins) > 0 and
len(internalPlugins[0]) > 0 and
internalPlugins[0][0]['API'] != PLUGIN_QUERY_API_VERSION):
internalCount = internalCountNew
internalPlugins = []
for i in range(pluginCountNew):
descInfo = gCarla.utils.get_cached_plugin_info(ptype, i)

for i in range(internalCountNew):
descInfo = gCarla.utils.get_cached_plugin_info(PLUGIN_INTERNAL, i)
plugins = checkPluginCached(descInfo, PLUGIN_INTERNAL)
if not descInfo['valid']:
continue

if plugins:
internalPlugins.append(plugins)
info = checkPluginCached(descInfo, ptype)

settingsDB.setValue("Plugins/Internal", internalPlugins)
if ptype == PLUGIN_SFZ:
info['filename'] = info['label']
info['label'] = info['name']

for plugins in internalPlugins:
for plugin in plugins:
self._addPluginToTable(plugin, self.tr("Internal"))
plugins.append(info)

del internalCountNew
del internalPlugins
if i % 50 == 0:
QApplication.processEvents(QEventLoop.ExcludeUserInputEvents, 50)

# ----------------------------------------------------------------------------------------------------
# Cached plugins (LV2)
settingsDB.setValue("Plugins/" + ptypeStr, plugins)
settingsDB.setValue("PluginCount/" + ptypeStr, pluginCount)

lv2Plugins = toList(settingsDB.value("Plugins/LV2", []))
# prepare rows in advance
self.ui.tableWidget.setRowCount(self.fLastTableIndex + len(plugins))

for plugins in lv2Plugins:
lv2Count += len(plugins)
for plugin in plugins:
self._addPluginToTable(plugin, ptypeStrTr)

lv2CountNew = gCarla.utils.get_cached_plugin_count(PLUGIN_LV2, LV2_PATH)
return pluginCount

if lv2CountNew != lv2Count or (len(lv2Plugins) > 0 and
len(lv2Plugins[0]) > 0 and
lv2Plugins[0][0]['API'] != PLUGIN_QUERY_API_VERSION):
lv2Count = lv2CountNew
lv2Plugins = []
def _reAddPlugins(self):
settingsDB = QSettings("falkTX", "CarlaPlugins4")

for i in range(lv2CountNew):
descInfo = gCarla.utils.get_cached_plugin_info(PLUGIN_LV2, i)
plugins = checkPluginCached(descInfo, PLUGIN_LV2)
for x in range(self.ui.tableWidget.rowCount()):
self.ui.tableWidget.removeRow(0)

if plugins:
lv2Plugins.append(plugins)
self.fLastTableIndex = 0
self.ui.tableWidget.setSortingEnabled(False)

settingsDB.setValue("Plugins/LV2", lv2Plugins)
settings = QSettings("falkTX", "Carla2")
LV2_PATH = splitter.join(toList(settings.value(CARLA_KEY_PATHS_LV2, CARLA_DEFAULT_LV2_PATH)))
SFZ_PATH = splitter.join(toList(settings.value(CARLA_KEY_PATHS_SFZ, CARLA_DEFAULT_SFZ_PATH)))
del settings

for plugins in lv2Plugins:
for plugin in plugins:
self._addPluginToTable(plugin, "LV2")
# ----------------------------------------------------------------------------------------------------
# plugins handled through backend

del lv2CountNew
del lv2Plugins
internalCount = self._reAddInternalHelper(settingsDB, PLUGIN_INTERNAL, "")
lv2Count = self._reAddInternalHelper(settingsDB, PLUGIN_LV2, LV2_PATH)
sfzCount = self._reAddInternalHelper(settingsDB, PLUGIN_SFZ, SFZ_PATH)

# ----------------------------------------------------------------------------------------------------
# LADSPA
@@ -1606,13 +1626,6 @@ class PluginDatabaseW(QDialog):
ladspaPlugins += toList(settingsDB.value("Plugins/LADSPA_win32", []))
ladspaPlugins += toList(settingsDB.value("Plugins/LADSPA_win64", []))

for plugins in ladspaPlugins:
for plugin in plugins:
self._addPluginToTable(plugin, "LADSPA")
ladspaCount += 1

del ladspaPlugins

# ----------------------------------------------------------------------------------------------------
# DSSI

@@ -1623,13 +1636,6 @@ class PluginDatabaseW(QDialog):
dssiPlugins += toList(settingsDB.value("Plugins/DSSI_win32", []))
dssiPlugins += toList(settingsDB.value("Plugins/DSSI_win64", []))

for plugins in dssiPlugins:
for plugin in plugins:
self._addPluginToTable(plugin, "DSSI")
dssiCount += 1

del dssiPlugins

# ----------------------------------------------------------------------------------------------------
# VST2

@@ -1640,43 +1646,58 @@ class PluginDatabaseW(QDialog):
vst2Plugins += toList(settingsDB.value("Plugins/VST2_win32", []))
vst2Plugins += toList(settingsDB.value("Plugins/VST2_win64", []))

for plugins in vst2Plugins:
for plugin in plugins:
self._addPluginToTable(plugin, "VST2")
vstCount += 1

del vst2Plugins

# ----------------------------------------------------------------------------------------------------
# Kits

sf2s = toList(settingsDB.value("Plugins/SF2", []))

for sf2 in sf2s:
for sf2_i in sf2:
self._addPluginToTable(sf2_i, "SF2")
kitCount += 1
# ----------------------------------------------------------------------------------------------------
# count plugins first, so we can create rows in advance

del sf2s
ladspaCount = 0
dssiCount = 0
vstCount = 0
sf2Count = 0

# ----------------------------------------------------------------------------------------------------
for plugins in ladspaPlugins:
ladspaCount += len(plugins)

sfzs = toList(settingsDB.value("Plugins/SFZ", []))
for plugins in dssiPlugins:
dssiCount += len(plugins)

for sfz in sfzs:
for sfz_i in sfz:
self._addPluginToTable(sfz_i, "SFZ")
kitCount += 1
for plugins in vst2Plugins:
vstCount += len(plugins)

del sfzs
for plugins in sf2s:
sf2Count += len(plugins)

self.ui.tableWidget.setRowCount(self.fLastTableIndex+ladspaCount+dssiCount+vstCount+sf2Count)

self.ui.label.setText(self.tr("Have %i Internal, %i LADSPA, %i DSSI, %i LV2 and %i VST plugins, plus %i Sound Kits" % (
internalCount, ladspaCount, dssiCount, lv2Count, vstCount, sf2Count+sfzCount)))

# ----------------------------------------------------------------------------------------------------
# now add all plugins to the table

self.ui.tableWidget.setSortingEnabled(True)
for plugins in ladspaPlugins:
for plugin in plugins:
self._addPluginToTable(plugin, "LADSPA")

self.ui.label.setText(self.tr("Have %i Internal, %i LADSPA, %i DSSI, %i LV2 and %i VST plugins, plus %i Sound Kits" % (
internalCount, ladspaCount, dssiCount, lv2Count, vstCount, kitCount)))
for plugins in dssiPlugins:
for plugin in plugins:
self._addPluginToTable(plugin, "DSSI")

for plugins in vst2Plugins:
for plugin in plugins:
self._addPluginToTable(plugin, "VST2")

for sf2 in sf2s:
for sf2_i in sf2:
self._addPluginToTable(sf2_i, "SF2")

# ----------------------------------------------------------------------------------------------------

self.ui.tableWidget.setSortingEnabled(True)
self._checkFilters()

# --------------------------------------------------------------------------------------------------------


+ 18
- 30
source/carla_host.py View File

@@ -27,11 +27,11 @@ from carla_config import *
import json

if config_UseQt5:
from PyQt5.QtCore import qCritical, QFileInfo, QModelIndex, QPointF, QTimer
from PyQt5.QtCore import qCritical, QEventLoop, QFileInfo, QModelIndex, QPointF, QTimer
from PyQt5.QtGui import QImage, QPalette
from PyQt5.QtWidgets import QAction, QApplication, QInputDialog, QFileSystemModel, QListWidgetItem, QMainWindow
else:
from PyQt4.QtCore import qCritical, QFileInfo, QModelIndex, QPointF, QTimer
from PyQt4.QtCore import qCritical, QEventLoop, QFileInfo, QModelIndex, QPointF, QTimer
from PyQt4.QtGui import QImage, QPalette
from PyQt4.QtGui import QAction, QApplication, QInputDialog, QFileSystemModel, QListWidgetItem, QMainWindow

@@ -338,9 +338,6 @@ class HostWindow(QMainWindow):
self.ui.dsb_transport_bpm.setEnabled(False)
self.ui.dsb_transport_bpm.setReadOnly(True)

if MACOS: # FIXME
self.ui.cb_transport_link.setEnabled(False)

self.ui.w_transport.setEnabled(False)

# ----------------------------------------------------------------------------------------------------
@@ -548,7 +545,7 @@ class HostWindow(QMainWindow):
self.ui.cb_transport_link.setVisible(False)

# Plugin needs to have timers always running so it receives messages
if self.host.isPlugin:
if self.host.isPlugin or self.host.isRemote:
self.startTimers()

# Qt needs this so it properly creates & resizes the canvas
@@ -1544,6 +1541,8 @@ class HostWindow(QMainWindow):
settings.setValue("HorizontalScrollBarValue", self.ui.graphicsView.horizontalScrollBar().value())
settings.setValue("VerticalScrollBarValue", self.ui.graphicsView.verticalScrollBar().value())

settings.setValue(CARLA_KEY_ENGINE_TRANSPORT_MODE, self.host.transportMode)

def loadSettings(self, firstTime):
settings = QSettings()

@@ -1618,17 +1617,6 @@ class HostWindow(QMainWindow):
else:
self.ui.act_add_jack.setVisible(False)

if not (self.host.isControl or self.host.isPlugin):
if self.ui.cb_transport_jack.isChecked():
transportMode = ENGINE_TRANSPORT_MODE_JACK
else:
transportMode = ENGINE_TRANSPORT_MODE_INTERNAL
transportExtra = ":link:" if self.ui.cb_transport_link.isChecked() else ""

self.enableTransport(transportMode != ENGINE_TRANSPORT_MODE_DISABLED)
self.host.transportMode = transportMode
self.host.set_engine_option(ENGINE_OPTION_TRANSPORT_MODE, transportMode, transportExtra)

self.fMiniCanvasUpdateTimeout = 1000 if self.fSavedSettings[CARLA_KEY_CANVAS_FANCY_EYE_CANDY] else 0

setEngineSettings(self.host)
@@ -1871,6 +1859,7 @@ class HostWindow(QMainWindow):
return
mode = ENGINE_TRANSPORT_MODE_JACK if clicked else ENGINE_TRANSPORT_MODE_INTERNAL
extra = ":link:" if self.ui.cb_transport_link.isChecked() else ""
self.host.transportMode = mode
self.host.set_engine_option(ENGINE_OPTION_TRANSPORT_MODE, mode, extra)

@pyqtSlot(bool)
@@ -1879,6 +1868,7 @@ class HostWindow(QMainWindow):
return
mode = ENGINE_TRANSPORT_MODE_JACK if self.ui.cb_transport_jack.isChecked() else ENGINE_TRANSPORT_MODE_INTERNAL
extra = ":link:" if clicked else ""
self.host.transportMode = mode
self.host.set_engine_option(ENGINE_OPTION_TRANSPORT_MODE, mode, extra)

# --------------------------------------------------------------------------------------------------------
@@ -1948,14 +1938,10 @@ class HostWindow(QMainWindow):
wasCompacted = pitem.isCompacted()
isCompacted = wasCompacted

for i in range(self.host.get_custom_data_count(pluginId)):
cdata = self.host.get_custom_data(pluginId, i)

if cdata['type'] == CUSTOM_DATA_TYPE_PROPERTY and cdata['key'] == "CarlaSkinIsCompacted":
isCompacted = bool(cdata['value'] == "true")
break
else:
check = self.host.get_custom_data_value(pluginId, CUSTOM_DATA_TYPE_PROPERTY, "CarlaSkinIsCompacted")
if not check:
return
isCompacted = bool(check == "true")

if wasCompacted == isCompacted:
return
@@ -2529,7 +2515,7 @@ def engineCallback(host, action, pluginId, value1, value2, value3, valueStr):
elif action == ENGINE_CALLBACK_NSM:
host.NSMCallback.emit(value1, value2, valueStr)
elif action == ENGINE_CALLBACK_IDLE:
QApplication.instance().processEvents()
QApplication.processEvents(QEventLoop.ExcludeUserInputEvents)
elif action == ENGINE_CALLBACK_INFO:
host.InfoCallback.emit(valueStr)
elif action == ENGINE_CALLBACK_ERROR:
@@ -2774,15 +2760,15 @@ def setHostSettings(host):
host.set_engine_option(ENGINE_OPTION_PREFER_PLUGIN_BRIDGES, host.preferPluginBridges, "")
host.set_engine_option(ENGINE_OPTION_PREFER_UI_BRIDGES, host.preferUIBridges, "")
host.set_engine_option(ENGINE_OPTION_PREVENT_BAD_BEHAVIOUR, host.preventBadBehaviour, "")
host.set_engine_option(ENGINE_OPTION_DEBUG_CONSOLE_OUTPUT, host.showLogs, "")
host.set_engine_option(ENGINE_OPTION_UI_BRIDGES_TIMEOUT, host.uiBridgesTimeout, "")
host.set_engine_option(ENGINE_OPTION_UIS_ALWAYS_ON_TOP, host.uisAlwaysOnTop, "")

if host.isPlugin or host.is_engine_running():
if host.isPlugin or host.isRemote or host.is_engine_running():
return

host.set_engine_option(ENGINE_OPTION_PROCESS_MODE, host.nextProcessMode, "")
host.set_engine_option(ENGINE_OPTION_TRANSPORT_MODE, host.transportMode, "")
host.set_engine_option(ENGINE_OPTION_DEBUG_CONSOLE_OUTPUT, host.showLogs, "")

# ------------------------------------------------------------------------------------------------------------
# Set Engine settings according to carla preferences. Returns selected audio driver.
@@ -2882,9 +2868,11 @@ def setEngineSettings(host):
# Only setup audio things if engine is not running
if not host.is_engine_running():
host.set_engine_option(ENGINE_OPTION_AUDIO_DEVICE, 0, audioDevice)
host.set_engine_option(ENGINE_OPTION_AUDIO_BUFFER_SIZE, audioBufferSize, "")
host.set_engine_option(ENGINE_OPTION_AUDIO_SAMPLE_RATE, audioSampleRate, "")
host.set_engine_option(ENGINE_OPTION_AUDIO_TRIPLE_BUFFER, audioTripleBuffer, "")

if not audioDriver.startswith("JACK"):
host.set_engine_option(ENGINE_OPTION_AUDIO_BUFFER_SIZE, audioBufferSize, "")
host.set_engine_option(ENGINE_OPTION_AUDIO_SAMPLE_RATE, audioSampleRate, "")
host.set_engine_option(ENGINE_OPTION_AUDIO_TRIPLE_BUFFER, audioTripleBuffer, "")

# --------------------------------------------------------------------------------------------------------
# fix things if needed


+ 38
- 38
source/carla_settings.py View File

@@ -52,6 +52,8 @@ SAMPLE_RATE_LIST = (22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000)
# Driver Settings

class DriverSettingsW(QDialog):
AUTOMATIC_OPTION = "(Auto)"

def __init__(self, parent, host, driverIndex, driverName):
QDialog.__init__(self, parent)
self.host = host
@@ -130,9 +132,17 @@ class DriverSettingsW(QDialog):
def slot_saveSettings(self):
settings = QSettings("falkTX", "Carla2")

bufferSize = self.ui.cb_buffersize.currentText()
sampleRate = self.ui.cb_samplerate.currentText()

if bufferSize == self.AUTOMATIC_OPTION:
bufferSize = "0"
if sampleRate == self.AUTOMATIC_OPTION:
sampleRate = "0"

settings.setValue("%s%s/Device" % (CARLA_KEY_ENGINE_DRIVER_PREFIX, self.fDriverName), self.ui.cb_device.currentText())
settings.setValue("%s%s/BufferSize" % (CARLA_KEY_ENGINE_DRIVER_PREFIX, self.fDriverName), self.ui.cb_buffersize.currentText())
settings.setValue("%s%s/SampleRate" % (CARLA_KEY_ENGINE_DRIVER_PREFIX, self.fDriverName), self.ui.cb_samplerate.currentText())
settings.setValue("%s%s/BufferSize" % (CARLA_KEY_ENGINE_DRIVER_PREFIX, self.fDriverName), bufferSize)
settings.setValue("%s%s/SampleRate" % (CARLA_KEY_ENGINE_DRIVER_PREFIX, self.fDriverName), sampleRate)
settings.setValue("%s%s/TripleBuffer" % (CARLA_KEY_ENGINE_DRIVER_PREFIX, self.fDriverName), self.ui.cb_triple_buffer.isChecked())

# --------------------------------------------------------------------------------------------------------
@@ -147,34 +157,39 @@ class DriverSettingsW(QDialog):
self.ui.cb_buffersize.clear()
self.ui.cb_samplerate.clear()

if deviceName:
driverDeviceInfo = self.host.get_engine_driver_device_info(self.fDriverIndex, deviceName)
driverDeviceHints = driverDeviceInfo['hints']
self.fBufferSizes = driverDeviceInfo['bufferSizes']
self.fSampleRates = driverDeviceInfo['sampleRates']
else:
driverDeviceHints = 0x0
self.fBufferSizes = BUFFER_SIZE_LIST
self.fSampleRates = SAMPLE_RATE_LIST
driverDeviceInfo = self.host.get_engine_driver_device_info(self.fDriverIndex, deviceName)
driverDeviceHints = driverDeviceInfo['hints']
self.fBufferSizes = driverDeviceInfo['bufferSizes']
self.fSampleRates = driverDeviceInfo['sampleRates']

if driverDeviceHints & ENGINE_DRIVER_DEVICE_CAN_TRIPLE_BUFFER:
self.ui.cb_triple_buffer.setEnabled(True)
else:
self.ui.cb_triple_buffer.setEnabled(False)

for bsize in self.fBufferSizes:
sbsize = str(bsize)
self.ui.cb_buffersize.addItem(sbsize)
if len(self.fBufferSizes) > 0:
for bsize in self.fBufferSizes:
sbsize = str(bsize)
self.ui.cb_buffersize.addItem(sbsize)

if oldBufferSize == sbsize:
self.ui.cb_buffersize.setCurrentIndex(self.ui.cb_buffersize.count()-1)
if oldBufferSize == sbsize:
self.ui.cb_buffersize.setCurrentIndex(self.ui.cb_buffersize.count()-1)

for srate in self.fSampleRates:
ssrate = str(int(srate))
self.ui.cb_samplerate.addItem(ssrate)
else:
self.ui.cb_buffersize.addItem(self.AUTOMATIC_OPTION)
self.ui.cb_buffersize.setCurrentIndex(0)

if len(self.fSampleRates) > 0:
for srate in self.fSampleRates:
ssrate = str(int(srate))
self.ui.cb_samplerate.addItem(ssrate)

if oldSampleRate == ssrate:
self.ui.cb_samplerate.setCurrentIndex(self.ui.cb_samplerate.count()-1)
if oldSampleRate == ssrate:
self.ui.cb_samplerate.setCurrentIndex(self.ui.cb_samplerate.count()-1)

else:
self.ui.cb_samplerate.addItem(self.AUTOMATIC_OPTION)
self.ui.cb_samplerate.setCurrentIndex(0)

# --------------------------------------------------------------------------------------------------------

@@ -281,19 +296,6 @@ class CarlaSettingsW(QDialog):
self.ui.ch_exp_prevent_bad_behaviour.setVisible(False)
self.ui.lw_page.hideRow(self.TAB_INDEX_WINE)

if not MACOS:
self.ui.label_engine_ui_bridges_mac_note.setVisible(False)

# FIXME, pipes on win32 not working, and mis-behaving on macOS
if MACOS or WINDOWS:
self.ui.ch_engine_prefer_ui_bridges.setChecked(False)
self.ui.ch_engine_prefer_ui_bridges.setEnabled(False)
self.ui.ch_engine_prefer_ui_bridges.setVisible(False)
self.ui.label_engine_ui_bridges_timeout.setEnabled(False)
self.ui.label_engine_ui_bridges_timeout.setVisible(False)
self.ui.sb_engine_ui_bridges_timeout.setEnabled(False)
self.ui.sb_engine_ui_bridges_timeout.setVisible(False)

# FIXME, not implemented yet
self.ui.ch_engine_uis_always_on_top.hide()

@@ -415,10 +417,10 @@ class CarlaSettingsW(QDialog):

if audioDriver == "JACK":
self.ui.sw_engine_process_mode.setCurrentIndex(0)
self.ui.tb_engine_driver_config.setEnabled(False)
else:
self.ui.sw_engine_process_mode.setCurrentIndex(1)
self.ui.tb_engine_driver_config.setEnabled(self.host.audioDriverForced is None and not self.host.isPlugin)

self.ui.tb_engine_driver_config.setEnabled(self.host.audioDriverForced is None and not self.host.isPlugin)

self.ui.cb_engine_process_mode_jack.setCurrentIndex(self.host.nextProcessMode)

@@ -866,10 +868,8 @@ class CarlaSettingsW(QDialog):
def slot_engineAudioDriverChanged(self):
if self.ui.cb_engine_audio_driver.currentText() == "JACK":
self.ui.sw_engine_process_mode.setCurrentIndex(0)
self.ui.tb_engine_driver_config.setEnabled(False)
else:
self.ui.sw_engine_process_mode.setCurrentIndex(1)
self.ui.tb_engine_driver_config.setEnabled(True)

@pyqtSlot()
def slot_showAudioDriverSettings(self):


+ 1
- 1
source/carla_shared.py View File

@@ -273,7 +273,7 @@ CARLA_DEFAULT_CANVAS_HQ_ANTIALIASING = False
# Engine
CARLA_DEFAULT_FORCE_STEREO = False
CARLA_DEFAULT_PREFER_PLUGIN_BRIDGES = False
CARLA_DEFAULT_PREFER_UI_BRIDGES = bool(not (MACOS or WINDOWS))
CARLA_DEFAULT_PREFER_UI_BRIDGES = True
CARLA_DEFAULT_MANAGE_UIS = True
CARLA_DEFAULT_UIS_ALWAYS_ON_TOP = False
CARLA_DEFAULT_MAX_PARAMETERS = MAX_DEFAULT_PARAMETERS


+ 6
- 0
source/carla_skin.py View File

@@ -527,6 +527,12 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta):
QLabel#label_name:disabled { color: #555; }
""" % styleSheet2

styleSheet += """
QComboBox#cb_presets,
QLabel#label_audio_in,
QLabel#label_audio_out,
QLabel#label_midi { font-size: 10px; }
"""
self.setStyleSheet(styleSheet)

# -------------------------------------------------------------


+ 9
- 0
source/carla_utils.py View File

@@ -90,6 +90,12 @@ CarlaPipeCallbackFunc = CFUNCTYPE(None, c_void_p, c_char_p)
# @see carla_get_cached_plugin_info()
class CarlaCachedPluginInfo(Structure):
_fields_ = [
# Wherever the data in this struct is valid.
# For performance reasons, plugins are only checked on request,
# and as such, the count vs number of really valid plugins might not match.
# Use this field to skip on plugins which cannot be loaded in Carla.
("valid", c_bool),

# Plugin category.
("category", c_enum),

@@ -133,6 +139,7 @@ class CarlaCachedPluginInfo(Structure):

# @see CarlaCachedPluginInfo
PyCarlaCachedPluginInfo = {
'valid': False,
'category': PLUGIN_CATEGORY_NONE,
'hints': 0x0,
'audioIns': 0,
@@ -270,6 +277,8 @@ class CarlaUtils(object):
return charPtrPtrToStringList(self.lib.carla_get_supported_features())

# Get how many internal plugins are available.
# Internal and LV2 plugin formats are cached and need to be discovered via this function.
# Do not call this for any other plugin formats.
def get_cached_plugin_count(self, ptype, pluginPath):
return int(self.lib.carla_get_cached_plugin_count(ptype, pluginPath.encode("utf-8")))



+ 3
- 12
source/discovery/Makefile View File

@@ -34,8 +34,8 @@ ifeq ($(MACOS),true)
BUILD_CXX_FLAGS += -ObjC++
endif

32BIT_FLAGS += -DBUILD_BRIDGE
64BIT_FLAGS += -DBUILD_BRIDGE
32BIT_FLAGS += -DBUILD_BRIDGE -DBUILD_BRIDGE_ALTERNATIVE_ARCH
64BIT_FLAGS += -DBUILD_BRIDGE -DBUILD_BRIDGE_ALTERNATIVE_ARCH

# ----------------------------------------------------------------------------------------------------------------------------

@@ -47,11 +47,6 @@ NATIVE_BUILD_FLAGS += $(FLUIDSYNTH_FLAGS)
NATIVE_LINK_FLAGS += $(FLUIDSYNTH_LIBS)
endif

ifeq ($(HAVE_LINUXSAMPLER),true)
NATIVE_BUILD_FLAGS += $(LINUXSAMPLER_FLAGS)
NATIVE_LINK_FLAGS += $(LINUXSAMPLER_LIBS)
endif

# ----------------------------------------------------------------------------------------------------------------------------

LIBS_native = $(MODULEDIR)/lilv.a
@@ -61,11 +56,7 @@ LIBS_win32 = $(MODULEDIR)/lilv.win32.a
LIBS_win64 = $(MODULEDIR)/lilv.win64.a
LINK_FLAGS += $(LILV_LIBS)

LIBS_native += $(MODULEDIR)/water.a
LIBS_posix32 += $(MODULEDIR)/water.posix32.a
LIBS_posix64 += $(MODULEDIR)/water.posix64.a
LIBS_win32 += $(MODULEDIR)/water.win32.a
LIBS_win64 += $(MODULEDIR)/water.win64.a
LIBS_native += $(MODULEDIR)/water.files.a
LINK_FLAGS += $(WATER_LIBS)

LINK_FLAGS += $(LIBDL_LIBS)


+ 65
- 162
source/discovery/carla-discovery.cpp View File

@@ -26,7 +26,6 @@
#endif

#include "CarlaLadspaUtils.hpp"
#include "CarlaDssiUtils.cpp"
#include "CarlaLv2Utils.hpp"
#include "CarlaVstUtils.hpp"

@@ -40,12 +39,13 @@

#include <iostream>

#include "water/files/File.h"
#include "water/text/StringArray.h"

#ifndef BUILD_BRIDGE
# define CARLA_UTILS_CACHED_PLUGINS_ONLY
# include "CarlaUtils.cpp"
# include "water/files/File.h"
# include "water/text/StringArray.h"
# include "CarlaDssiUtils.cpp"
# include "../backend/utils/CachedPlugins.cpp"
#else
# include "CarlaDssiUtils.hpp"
#endif

#define DISCOVERY_OUT(x, y) std::cout << "\ncarla-discovery::" << x << "::" << y << std::endl;
@@ -270,9 +270,43 @@ static intptr_t VSTCALLBACK vstHostCallback(AEffect* const effect, const int32_t
// ------------------------------ Plugin Checks -----------------------------

#ifndef BUILD_BRIDGE
static void print_cached_plugin(const CarlaCachedPluginInfo* const pinfo)
{
if (! pinfo->valid)
return;

DISCOVERY_OUT("init", "-----------");
DISCOVERY_OUT("build", BINARY_NATIVE);
DISCOVERY_OUT("hints", pinfo->hints);
DISCOVERY_OUT("name", pinfo->name);
DISCOVERY_OUT("maker", pinfo->maker);
DISCOVERY_OUT("label", pinfo->label);
DISCOVERY_OUT("audio.ins", pinfo->audioIns);
DISCOVERY_OUT("audio.outs", pinfo->audioOuts);
DISCOVERY_OUT("midi.ins", pinfo->midiIns);
DISCOVERY_OUT("midi.outs", pinfo->midiOuts);
DISCOVERY_OUT("parameters.ins", pinfo->parameterIns);
DISCOVERY_OUT("parameters.outs", pinfo->parameterOuts);
DISCOVERY_OUT("end", "------------");
}

static void do_cached_check(const PluginType type)
{
const char* const plugPath = (type == PLUGIN_LV2) ? std::getenv("LV2_PATH") : nullptr;
const char* plugPath;

switch (type)
{
case PLUGIN_LV2:
plugPath = std::getenv("LV2_PATH");
break;
case PLUGIN_SFZ:
plugPath = std::getenv("SFZ_PATH");
break;
default:
plugPath = nullptr;
break;
}

const uint count = carla_get_cached_plugin_count(type, plugPath);

for (uint i=0; i<count; ++i)
@@ -280,19 +314,7 @@ static void do_cached_check(const PluginType type)
const CarlaCachedPluginInfo* pinfo(carla_get_cached_plugin_info(type, i));
CARLA_SAFE_ASSERT_CONTINUE(pinfo != nullptr);

DISCOVERY_OUT("init", "-----------");
DISCOVERY_OUT("build", BINARY_NATIVE);
DISCOVERY_OUT("hints", pinfo->hints);
DISCOVERY_OUT("name", pinfo->name);
DISCOVERY_OUT("maker", pinfo->maker);
DISCOVERY_OUT("label", pinfo->label);
DISCOVERY_OUT("audio.ins", pinfo->audioIns);
DISCOVERY_OUT("audio.outs", pinfo->audioOuts);
DISCOVERY_OUT("midi.ins", pinfo->midiIns);
DISCOVERY_OUT("midi.outs", pinfo->midiOuts);
DISCOVERY_OUT("parameters.ins", pinfo->parameterIns);
DISCOVERY_OUT("parameters.outs", pinfo->parameterOuts);
DISCOVERY_OUT("end", "------------");
print_cached_plugin(pinfo);
}
}
#endif
@@ -674,11 +696,13 @@ static void do_dssi_check(lib_t& libHandle, const char* const filename, const bo
if (midiIns > 0 && audioIns == 0 && audioOuts > 0)
hints |= PLUGIN_IS_SYNTH;

#ifndef BUILD_BRIDGE
if (const char* const ui = find_dssi_ui(filename, ldescriptor->Label))
{
hints |= PLUGIN_HAS_CUSTOM_UI;
delete[] ui;
}
#endif

if (doInit)
{
@@ -828,6 +852,7 @@ static void do_dssi_check(lib_t& libHandle, const char* const filename, const bo
}
}

#ifndef BUILD_BRIDGE
static void do_lv2_check(const char* const bundle, const bool doInit)
{
Lv2WorldClass& lv2World(Lv2WorldClass::getInstance());
@@ -866,17 +891,18 @@ static void do_lv2_check(const char* const bundle, const bool doInit)
// Get & check every plugin-instance
for (int i=0, count=URIs.size(); i < count; ++i)
{
const LV2_RDF_Descriptor* const rdfDescriptor(lv2_rdf_new(URIs[i].toRawUTF8(), false));
const char* const URI = URIs[i].toRawUTF8();
ScopedPointer<const LV2_RDF_Descriptor> rdfDescriptor(lv2_rdf_new(URI, false));

if (rdfDescriptor == nullptr || rdfDescriptor->URI == nullptr)
{
DISCOVERY_OUT("error", "Failed to find LV2 plugin '" << URIs[i].toRawUTF8() << "'");
DISCOVERY_OUT("error", "Failed to find LV2 plugin '" << URI << "'");
continue;
}

if (doInit)
{
// test if DLL is loadable, twice
// test if lib is loadable, twice
const lib_t libHandle1 = lib_open(rdfDescriptor->Binary);

if (libHandle1 == nullptr)
@@ -900,139 +926,16 @@ static void do_lv2_check(const char* const bundle, const bool doInit)
lib_close(libHandle2);
}

// test if we support all required ports and features
{
bool supported = true;

for (uint32_t j=0; j < rdfDescriptor->PortCount && supported; ++j)
{
const LV2_RDF_Port* const rdfPort(&rdfDescriptor->Ports[j]);

if (is_lv2_port_supported(rdfPort->Types))
{
pass();
}
else if (! LV2_IS_PORT_OPTIONAL(rdfPort->Properties))
{
DISCOVERY_OUT("error", "Plugin '" << rdfDescriptor->URI << "' requires a non-supported port type (portName: '" << rdfPort->Name << "')");
supported = false;
break;
}
}

for (uint32_t j=0; j < rdfDescriptor->FeatureCount && supported; ++j)
{
const LV2_RDF_Feature& feature(rdfDescriptor->Features[j]);

if (std::strcmp(feature.URI, LV2_DATA_ACCESS_URI) == 0 || std::strcmp(feature.URI, LV2_INSTANCE_ACCESS_URI) == 0)
{
DISCOVERY_OUT("warning", "Plugin '" << rdfDescriptor->URI << "' DSP wants UI feature '" << feature.URI << "', ignoring this");
}
else if (feature.Required && ! is_lv2_feature_supported(feature.URI))
{
DISCOVERY_OUT("error", "Plugin '" << rdfDescriptor->URI << "' requires a non-supported feature '" << feature.URI << "'");
supported = false;
break;
}
}

if (! supported)
{
delete rdfDescriptor;
continue;
}
}

uint hints = 0x0;
int audioIns = 0;
int audioOuts = 0;
int midiIns = 0;
int midiOuts = 0;
int parametersIns = 0;
int parametersOuts = 0;

for (uint32_t j=0; j < rdfDescriptor->FeatureCount; ++j)
{
const LV2_RDF_Feature* const rdfFeature(&rdfDescriptor->Features[j]);

if (std::strcmp(rdfFeature->URI, LV2_CORE__hardRTCapable) == 0)
hints |= PLUGIN_IS_RTSAFE;
}

for (uint32_t j=0; j < rdfDescriptor->PortCount; ++j)
{
const LV2_RDF_Port* const rdfPort(&rdfDescriptor->Ports[j]);

if (LV2_IS_PORT_AUDIO(rdfPort->Types))
{
if (LV2_IS_PORT_INPUT(rdfPort->Types))
audioIns += 1;
else if (LV2_IS_PORT_OUTPUT(rdfPort->Types))
audioOuts += 1;
}
else if (LV2_IS_PORT_CONTROL(rdfPort->Types))
{
if (LV2_IS_PORT_DESIGNATION_LATENCY(rdfPort->Designation))
{
pass();
}
else if (LV2_IS_PORT_DESIGNATION_SAMPLE_RATE(rdfPort->Designation))
{
pass();
}
else if (LV2_IS_PORT_DESIGNATION_FREEWHEELING(rdfPort->Designation))
{
pass();
}
else if (LV2_IS_PORT_DESIGNATION_TIME(rdfPort->Designation))
{
pass();
}
else
{
if (LV2_IS_PORT_INPUT(rdfPort->Types))
parametersIns += 1;
else if (LV2_IS_PORT_OUTPUT(rdfPort->Types))
parametersOuts += 1;
}
}
else if (LV2_PORT_SUPPORTS_MIDI_EVENT(rdfPort->Types))
{
if (LV2_IS_PORT_INPUT(rdfPort->Types))
midiIns += 1;
else if (LV2_IS_PORT_OUTPUT(rdfPort->Types))
midiOuts += 1;
}
}

if (LV2_IS_INSTRUMENT(rdfDescriptor->Type[0], rdfDescriptor->Type[1]))
hints |= PLUGIN_IS_SYNTH;

if (rdfDescriptor->UICount > 0)
hints |= PLUGIN_HAS_CUSTOM_UI;

DISCOVERY_OUT("init", "-----------");
DISCOVERY_OUT("build", BINARY_NATIVE);
DISCOVERY_OUT("hints", hints);
const LilvPlugin* const cPlugin(lv2World.getPluginFromURI(URI));
CARLA_SAFE_ASSERT_CONTINUE(cPlugin != nullptr);

if (rdfDescriptor->Name != nullptr)
DISCOVERY_OUT("name", rdfDescriptor->Name);
if (rdfDescriptor->Author != nullptr)
DISCOVERY_OUT("maker", rdfDescriptor->Author);
Lilv::Plugin lilvPlugin(cPlugin);
CARLA_SAFE_ASSERT_CONTINUE(lilvPlugin.get_uri().is_uri());

DISCOVERY_OUT("uri", rdfDescriptor->URI);
DISCOVERY_OUT("uniqueId", rdfDescriptor->UniqueID);
DISCOVERY_OUT("audio.ins", audioIns);
DISCOVERY_OUT("audio.outs", audioOuts);
DISCOVERY_OUT("midi.ins", midiIns);
DISCOVERY_OUT("midi.outs", midiOuts);
DISCOVERY_OUT("parameters.ins", parametersIns);
DISCOVERY_OUT("parameters.outs", parametersOuts);
DISCOVERY_OUT("end", "------------");

delete rdfDescriptor;
print_cached_plugin(get_cached_plugin_lv2(lv2World, lilvPlugin));
}
}
#endif

static void do_vst_check(lib_t& libHandle, const char* const filename, const bool doInit)
{
@@ -1040,7 +943,7 @@ static void do_vst_check(lib_t& libHandle, const char* const filename, const boo

#ifdef CARLA_OS_MAC
CFBundleRef bundleRef = nullptr;
CFBundleRefNum resFileId;
CFBundleRefNum resFileId = 0;

if (libHandle == nullptr)
{
@@ -1476,18 +1379,16 @@ int main(int argc, char* argv[])
break;
}

if (type != PLUGIN_SF2 && type != PLUGIN_SFZ)
if (type != PLUGIN_SF2 && filenameCheck.contains("fluidsynth", true))
{
if (filenameCheck.contains("fluidsynth", true))
{
DISCOVERY_OUT("info", "skipping fluidsynth based plugin");
return 0;
}
DISCOVERY_OUT("info", "skipping fluidsynth based plugin");
return 0;
}

#ifdef CARLA_OS_MAC
if (type == PLUGIN_VST2 && (filenameCheck.endsWith(".vst") || filenameCheck.endsWith(".vst/")))
openLib = false;
if (type == PLUGIN_VST2 && (filenameCheck.endsWith(".vst") || filenameCheck.endsWith(".vst/")))
openLib = false;
#endif
}

if (openLib)
{
@@ -1540,9 +1441,11 @@ int main(int argc, char* argv[])
case PLUGIN_DSSI:
do_dssi_check(handle, filename, doInit);
break;
#ifndef BUILD_BRIDGE
case PLUGIN_LV2:
do_lv2_check(filename, doInit);
break;
#endif
case PLUGIN_VST2:
do_vst_check(handle, filename, doInit);
break;


+ 2
- 1
source/externalui.py View File

@@ -200,7 +200,8 @@ class ExternalUI(object):
line2 = "%.10f" % line
else:
print("unknown data type to send:", type(line))
return False
hasError = True
break

if not gCarla.utils.pipe_client_write_msg(self.fPipeClient, line2 + "\n"):
hasError = True


+ 5
- 0
source/libjack/libjack.cpp View File

@@ -571,6 +571,11 @@ bool CarlaJackAppClient::handleRtData()
}

case kPluginBridgeRtClientProcess: {
const uint32_t frames(fShmRtClientControl.readUInt());
CARLA_SAFE_ASSERT_UINT2_BREAK(frames == fServer.bufferSize, frames, fServer.bufferSize);

// TODO tell client of xrun in case buffersize does not match

// FIXME - lock if offline
const CarlaMutexTryLocker cmtl(fRealtimeThreadMutex);



+ 1
- 1
source/modules/dgl/src/NanoVG.cpp View File

@@ -863,7 +863,7 @@ float NanoVG::textBounds(float x, float y, const char* string, const char* end,
if (fContext == nullptr) return 0.0f;
DISTRHO_SAFE_ASSERT_RETURN(string != nullptr && string[0] != '\0', 0.0f);

float b[4];
float b[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
const float ret = nvgTextBounds(fContext, x, y, string, end, b);
bounds = Rectangle<float>(b[0], b[1], b[2] - b[0], b[3] - b[1]);
return ret;


+ 1
- 0
source/modules/dgl/src/Window.cpp View File

@@ -396,6 +396,7 @@ struct Window::PrivateData {
#elif defined(DISTRHO_OS_MAC)
if (mWindow != nullptr)
[mWindow makeKeyWindow];
[NSApp activateIgnoringOtherApps:YES];
#else
XRaiseWindow(xDisplay, xWindow);
XSetInputFocus(xDisplay, xWindow, RevertToPointerRoot, CurrentTime);


+ 2
- 2
source/modules/dgl/src/nanovg/fontstash.h View File

@@ -1226,7 +1226,7 @@ float fonsDrawText(FONScontext* stash,
const char* str, const char* end)
{
FONSstate* state = fons__getState(stash);
unsigned int codepoint;
unsigned int codepoint = 0;
unsigned int utf8state = 0;
FONSglyph* glyph = NULL;
FONSquad q;
@@ -1411,7 +1411,7 @@ float fonsTextBounds(FONScontext* stash,
float* bounds)
{
FONSstate* state = fons__getState(stash);
unsigned int codepoint;
unsigned int codepoint = 0;
unsigned int utf8state = 0;
FONSquad q;
FONSglyph* glyph = NULL;


+ 4
- 2
source/modules/rtaudio/RtAudio.cpp View File

@@ -2218,8 +2218,10 @@ bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
}
}

// auto-connect "device" is #1
shouldAutoconnect_ = device == 0;
// auto-connect-off "device" is at index 1
shouldAutoconnect_ = (device != 1 &&
std::getenv("LADISH_APP_NAME") == nullptr &&
std::getenv("NSM_URL") == nullptr);

// Setup the buffer conversion information structure. We don't use
// buffers to do channel offsets, so we override that parameter


+ 1
- 1
source/modules/sfzero/sfzero/SFZReader.cpp View File

@@ -516,7 +516,7 @@ const char *Reader::readPathInto(water::String *pathOut, const char *pIn, const

int Reader::keyValue(const water::String &str)
{
auto chars = str.toRawUTF8();
const char* const chars = str.toRawUTF8();

char c = chars[0];



+ 2
- 2
source/modules/sfzero/sfzero/SFZSound.cpp View File

@@ -123,7 +123,7 @@ Region *Sound::regionAt(int index) { return regions_[index]; }
water::String Sound::dump()
{
water::String info;
auto &errors = getErrors();
const water::StringArray& errors = getErrors();
if (errors.size() > 0)
{
info << errors.size() << " errors: \n";
@@ -135,7 +135,7 @@ water::String Sound::dump()
info << "no errors.\n\n";
}

auto &warnings = getWarnings();
const water::StringArray& warnings = getWarnings();
if (warnings.size() > 0)
{
info << warnings.size() << " warnings: \n";


+ 10
- 0
source/modules/water/common.hpp View File

@@ -21,6 +21,7 @@
#ifdef CARLA_OS_MAC
# include "text/String.h"
# import <Foundation/NSAutoreleasePool.h>
# import <Foundation/NSString.h>
#endif
@@ -90,6 +91,15 @@ NSString* waterStringToNS (const String& s)
{
return [NSString stringWithUTF8String: s.toUTF8()];
}
class AutoNSAutoreleasePool {
public:
AutoNSAutoreleasePool() : pool([NSAutoreleasePool new]) {}
~AutoNSAutoreleasePool() { [pool drain]; }
private:
NSAutoreleasePool* const pool;
};
#endif
}

+ 1
- 0
source/modules/water/containers/Array.h View File

@@ -69,6 +69,7 @@ public:
@param other the array to copy
*/
Array (const Array<ElementType>& other) noexcept
: numUsed (0)
{
CARLA_SAFE_ASSERT_RETURN(data.setAllocatedSize (other.numUsed),);
numUsed = other.numUsed;


+ 63
- 67
source/modules/water/files/File.cpp View File

@@ -1392,9 +1392,7 @@ static NSString* getFileLink (const String& path)
bool File::isSymbolicLink() const
{
// FIXME
return false;
//return getFileLink (fullPath) != nil;
return getFileLink (fullPath) != nil;
}
File File::getLinkedTarget() const
@@ -1407,62 +1405,62 @@ File File::getLinkedTarget() const
bool File::copyInternal (const File& dest) const
{
//@autoreleasepool
{
NSFileManager* fm = [NSFileManager defaultManager];
return [fm fileExistsAtPath: waterStringToNS (fullPath)]
#if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
&& [fm copyItemAtPath: waterStringToNS (fullPath)
toPath: waterStringToNS (dest.getFullPathName())
error: nil];
#else
&& [fm copyPath: waterStringToNS (fullPath)
toPath: waterStringToNS (dest.getFullPathName())
handler: nil];
#endif
}
const AutoNSAutoreleasePool arpool;
NSFileManager* fm = [NSFileManager defaultManager];
return [fm fileExistsAtPath: waterStringToNS (fullPath)]
#if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
&& [fm copyItemAtPath: waterStringToNS (fullPath)
toPath: waterStringToNS (dest.getFullPathName())
error: nil];
#else
&& [fm copyPath: waterStringToNS (fullPath)
toPath: waterStringToNS (dest.getFullPathName())
handler: nil];
#endif
}
File File::getSpecialLocation (const SpecialLocationType type)
{
//@autoreleasepool
{
String resultPath;
const AutoNSAutoreleasePool arpool;
switch (type)
{
case userHomeDirectory: resultPath = nsStringToWater (NSHomeDirectory()); break;
String resultPath;
case tempDirectory:
{
File tmp ("~/Library/Caches/" + water_getExecutableFile().getFileNameWithoutExtension());
tmp.createDirectory();
return File (tmp.getFullPathName());
}
switch (type)
{
case userHomeDirectory:
resultPath = nsStringToWater (NSHomeDirectory());
break;
case currentExecutableFile:
return water_getExecutableFile();
case tempDirectory:
{
File tmp ("~/Library/Caches/" + water_getExecutableFile().getFileNameWithoutExtension());
tmp.createDirectory();
return File (tmp.getFullPathName());
}
case hostApplicationPath:
{
unsigned int size = 8192;
HeapBlock<char> buffer;
buffer.calloc (size + 8);
case currentExecutableFile:
return water_getExecutableFile();
_NSGetExecutablePath (buffer.getData(), &size);
return File (String::fromUTF8 (buffer, (int) size));
}
case hostApplicationPath:
{
unsigned int size = 8192;
HeapBlock<char> buffer;
buffer.calloc (size + 8);
default:
jassertfalse; // unknown type?
break;
_NSGetExecutablePath (buffer.getData(), &size);
return File (String::fromUTF8 (buffer, (int) size));
}
if (resultPath.isNotEmpty())
return File (resultPath.convertToPrecomposedUnicode());
default:
jassertfalse; // unknown type?
break;
}
if (resultPath.isNotEmpty())
return File (resultPath.convertToPrecomposedUnicode());
return File();
}
//==============================================================================
@@ -1474,10 +1472,9 @@ public:
wildCard (wildCard_),
enumerator (nil)
{
//@autoreleasepool
{
enumerator = [[[NSFileManager defaultManager] enumeratorAtPath: waterStringToNS (directory.getFullPathName())] retain];
}
const AutoNSAutoreleasePool arpool;
enumerator = [[[NSFileManager defaultManager] enumeratorAtPath: waterStringToNS (directory.getFullPathName())] retain];
}
~Pimpl()
@@ -1489,30 +1486,29 @@ public:
bool* const isDir, int64* const fileSize,
Time* const modTime, Time* const creationTime, bool* const isReadOnly)
{
//@autoreleasepool
{
const char* wildcardUTF8 = nullptr;
const AutoNSAutoreleasePool arpool;
for (;;)
{
NSString* file;
if (enumerator == nil || (file = [enumerator nextObject]) == nil)
return false;
const char* wildcardUTF8 = nullptr;
[enumerator skipDescendents];
filenameFound = nsStringToWater (file).convertToPrecomposedUnicode();
for (;;)
{
NSString* file;
if (enumerator == nil || (file = [enumerator nextObject]) == nil)
return false;
if (wildcardUTF8 == nullptr)
wildcardUTF8 = wildCard.toUTF8();
[enumerator skipDescendents];
filenameFound = nsStringToWater (file).convertToPrecomposedUnicode();
if (fnmatch (wildcardUTF8, filenameFound.toUTF8(), FNM_CASEFOLD) != 0)
continue;
if (wildcardUTF8 == nullptr)
wildcardUTF8 = wildCard.toUTF8();
const String fullPath (parentDir + filenameFound);
updateStatInfoForFile (fullPath, isDir, fileSize, modTime, creationTime, isReadOnly);
if (fnmatch (wildcardUTF8, filenameFound.toUTF8(), FNM_CASEFOLD) != 0)
continue;
return true;
}
const String fullPath (parentDir + filenameFound);
updateStatInfoForFile (fullPath, isDir, fileSize, modTime, creationTime, isReadOnly);
return true;
}
}


+ 3
- 3
source/modules/water/water.files.cpp View File

@@ -1,7 +1,7 @@
/*
* Cross-platform C++ library for Carla, based on Juce v4
* Copyright (C) 2015 ROLI Ltd.
* Copyright (C) 2017 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2017-2018 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -36,20 +36,20 @@ HINSTANCE water_getCurrentModuleInstanceHandle() noexcept
}
#include "files/DirectoryIterator.cpp"
#include "files/File.cpp"
#include "misc/Result.cpp"
#include "misc/Time.cpp"
#include "text/CharacterFunctions.cpp"
#include "text/StringArray.cpp"
#include "text/String.cpp"
#if defined(DEBUG) || defined(BUILDING_CARLA_FOR_WINDOWS)
# include "files/DirectoryIterator.cpp"
# include "files/FileInputStream.cpp"
# include "files/FileOutputStream.cpp"
# include "files/TemporaryFile.cpp"
# include "maths/Random.cpp"
# include "memory/MemoryBlock.cpp"
# include "misc/Time.cpp"
# include "streams/InputStream.cpp"
# include "streams/MemoryOutputStream.cpp"
# include "streams/OutputStream.cpp"


+ 4
- 6
source/native-plugins/_all.all.c View File

@@ -68,20 +68,18 @@ void carla_register_all_native_plugins(void)
// Audio file
carla_register_native_plugin_audiofile();

// MIDI file and sequencer
// MIDI file
carla_register_native_plugin_midifile();
#ifdef HAVE_PYQT
carla_register_native_plugin_midipattern();
#endif

// Carla
#ifdef HAVE_PYQT
// Carla
carla_register_native_plugin_carla();
#endif

// External-UI plugins
carla_register_native_plugin_bigmeter();
carla_register_native_plugin_midipattern();
carla_register_native_plugin_notes();
#endif // HAVE_PYQT

#ifdef HAVE_EXTERNAL_PLUGINS
// Experimental plugins


+ 2
- 4
source/native-plugins/_all.base.c View File

@@ -57,16 +57,14 @@ void carla_register_all_native_plugins(void)
carla_register_native_plugin_miditranspose();

#ifdef HAVE_PYQT
// MIDI sequencer
carla_register_native_plugin_midipattern();

// Carla
carla_register_native_plugin_carla();
#endif

// External-UI plugins
carla_register_native_plugin_bigmeter();
carla_register_native_plugin_midipattern();
carla_register_native_plugin_notes();
#endif
}

// --------------------------------------------------------------------------------------------------------------------

+ 2
- 2
source/native-plugins/_data.base.cpp View File

@@ -338,7 +338,7 @@ static const NativePluginDescriptor sNativePluginDescriptors[] = {
/* copyright */ "GNU GPL v2+",
DESCFUNCS
},
#endif
#endif // HAVE_PYQT

// --------------------------------------------------------------------------------------------------------------------
// External-UI plugins
@@ -379,7 +379,7 @@ static const NativePluginDescriptor sNativePluginDescriptors[] = {
/* copyright */ "GNU GPL v2+",
DESCFUNCS
},
#endif
#endif // HAVE_PYQT

};



+ 2
- 2
source/native-plugins/audio-base.hpp View File

@@ -41,8 +41,8 @@ struct AudioFilePool {
size(0) {}
#else
AudioFilePool()
: startFrame(0),
sampleRate(0),
: sampleRate(0),
startFrame(0),
size(0)
{
buffer[0] = buffer[1] = nullptr;


+ 3
- 0
source/native-plugins/resources/carla-plugin View File

@@ -74,6 +74,9 @@ class PluginHost(CarlaHostQtPlugin):
self.fExternalUI.idleExternalUI()

def is_engine_running(self):
if self.fExternalUI is None:
return False

return self.fExternalUI.isRunning()

def set_engine_about_to_close(self):


+ 109
- 255
source/patchcanvas.py View File

@@ -760,15 +760,7 @@ def getGroupPos(group_id, port_mode=PORT_MODE_OUTPUT):

for group in canvas.group_list:
if group.group_id == group_id:
if group.split:
if port_mode == PORT_MODE_OUTPUT:
return group.widgets[0].pos()
elif port_mode == PORT_MODE_INPUT:
return group.widgets[1].pos()
else:
return QPointF(0, 0)
else:
return group.widgets[0].pos()
return group.widgets[1 if (group.split and port_mode == PORT_MODE_INPUT) else 0].pos()

qCritical("PatchCanvas::getGroupPos(%i, %s) - unable to find group" % (group_id, port_mode2str(port_mode)))
return QPointF(0, 0)
@@ -1266,26 +1258,16 @@ class PatchScene(QGraphicsScene):
rect = item.boundingRect()

if first_value:
first_value = False
min_x = pos.x()
elif pos.x() < min_x:
min_x = pos.x()

if first_value:
min_y = pos.y()
elif pos.y() < min_y:
min_y = pos.y()

if first_value:
max_x = pos.x() + rect.width()
elif pos.x() + rect.width() > max_x:
max_x = pos.x() + rect.width()

if first_value:
max_y = pos.y() + rect.height()
elif pos.y() + rect.height() > max_y:
max_y = pos.y() + rect.height()

first_value = False
else:
min_x = min(min_x, pos.x())
min_y = min(min_y, pos.y())
max_x = max(max_x, pos.x() + rect.width())
max_y = max(max_y, pos.y() + rect.height())

if not first_value:
self.m_view.fitInView(min_x, min_y, abs(max_x - min_x), abs(max_y - min_y), Qt.KeepAspectRatio)
@@ -1390,19 +1372,14 @@ class PatchScene(QGraphicsScene):
self.m_rubberband_orig_point = event.scenePos()

pos = event.scenePos()

if pos.x() > self.m_rubberband_orig_point.x():
x = self.m_rubberband_orig_point.x()
else:
x = pos.x()

if pos.y() > self.m_rubberband_orig_point.y():
y = self.m_rubberband_orig_point.y()
else:
y = pos.y()
x = min(pos.x(), self.m_rubberband_orig_point.x())
y = min(pos.y(), self.m_rubberband_orig_point.y())

lineHinting = canvas.theme.rubberband_pen.widthF() / 2
self.m_rubberband.setRect(x+lineHinting, y+lineHinting, abs(pos.x() - self.m_rubberband_orig_point.x()), abs(pos.y() - self.m_rubberband_orig_point.y()))
self.m_rubberband.setRect(x+lineHinting,
y+lineHinting,
abs(pos.x() - self.m_rubberband_orig_point.x()),
abs(pos.y() - self.m_rubberband_orig_point.y()))
return event.accept()

if self.m_mid_button_down and self.m_ctrl_down:
@@ -1570,10 +1547,12 @@ class CanvasLine(QGraphicsLineItem):

def updateLinePos(self):
if self.item1.getPortMode() == PORT_MODE_OUTPUT:
line = QLineF(self.item1.scenePos().x() + self.item1.getPortWidth() + 12,
self.item1.scenePos().y() + float(canvas.theme.port_height)/2,
self.item2.scenePos().x(),
self.item2.scenePos().y() + float(canvas.theme.port_height)/2)
rect1 = self.item1.sceneBoundingRect()
rect2 = self.item2.sceneBoundingRect()
line = QLineF(rect1.right(),
rect1.top() + float(canvas.theme.port_height)/2,
rect2.left(),
rect2.top() + float(canvas.theme.port_height)/2)
self.setLine(line)

self.m_lineSelected = False
@@ -1669,17 +1648,15 @@ class CanvasBezierLine(QGraphicsPathItem):

def updateLinePos(self):
if self.item1.getPortMode() == PORT_MODE_OUTPUT:
item1_x = self.item1.scenePos().x() + self.item1.getPortWidth() + 12
item1_y = self.item1.scenePos().y() + float(canvas.theme.port_height)/2

item2_x = self.item2.scenePos().x()
item2_y = self.item2.scenePos().y() + float(canvas.theme.port_height)/2
rect1 = self.item1.sceneBoundingRect()
rect2 = self.item2.sceneBoundingRect()

item1_mid_x = abs(item1_x - item2_x) / 2
item1_new_x = item1_x + item1_mid_x

item2_mid_x = abs(item1_x - item2_x) / 2
item2_new_x = item2_x - item2_mid_x
item1_x = rect1.right()
item2_x = rect2.left()
item1_y = rect1.top() + float(canvas.theme.port_height)/2
item2_y = rect2.top() + float(canvas.theme.port_height)/2
item1_new_x = item1_x + abs(item1_x - item2_x) / 2
item2_new_x = item2_x - abs(item1_x - item2_x) / 2

path = QPainterPath(QPointF(item1_x, item1_y))
path.cubicTo(item1_new_x, item1_y, item2_new_x, item2_y, item2_x, item2_y)
@@ -2092,11 +2069,14 @@ class CanvasPort(QGraphicsItem):
painter.save()
painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing == ANTIALIASING_FULL))

# FIXME: would be more correct is to take line width from Pen, loaded to painter,
# FIXME: would be more correct to take line width from Pen, loaded to painter,
# but this needs some code rearrangement
lineHinting = canvas.theme.port_audio_jack_pen.widthF() / 2

poly_locx = [0, 0, 0, 0, 0]
poly_corner_xhinting = (float(canvas.theme.port_height)/2) % floor(float(canvas.theme.port_height)/2)
if poly_corner_xhinting == 0:
poly_corner_xhinting = 0.5 * (1 - 7 / (float(canvas.theme.port_height)/2))

if self.m_port_mode == PORT_MODE_INPUT:
text_pos = QPointF(3, canvas.theme.port_text_ypos)
@@ -2104,7 +2084,7 @@ class CanvasPort(QGraphicsItem):
if canvas.theme.port_mode == Theme.THEME_PORT_POLYGON:
poly_locx[0] = lineHinting
poly_locx[1] = self.m_port_width + 5 - lineHinting
poly_locx[2] = self.m_port_width + 12 - lineHinting
poly_locx[2] = self.m_port_width + 12 - poly_corner_xhinting
poly_locx[3] = self.m_port_width + 5 - lineHinting
poly_locx[4] = lineHinting
elif canvas.theme.port_mode == Theme.THEME_PORT_SQUARE:
@@ -2123,7 +2103,7 @@ class CanvasPort(QGraphicsItem):
if canvas.theme.port_mode == Theme.THEME_PORT_POLYGON:
poly_locx[0] = self.m_port_width + 12 - lineHinting
poly_locx[1] = 7 + lineHinting
poly_locx[2] = 0 + lineHinting
poly_locx[2] = 0 + poly_corner_xhinting
poly_locx[3] = 7 + lineHinting
poly_locx[4] = self.m_port_width + 12 - lineHinting
elif canvas.theme.port_mode == Theme.THEME_PORT_SQUARE:
@@ -2407,23 +2387,9 @@ class CanvasBox(QGraphicsItem):
def updatePositions(self):
self.prepareGeometryChange()

max_in_width = 0
max_in_height = canvas.theme.box_header_height + canvas.theme.box_header_spacing
max_out_width = 0
max_out_height = canvas.theme.box_header_height + canvas.theme.box_header_spacing
port_spacing = canvas.theme.port_height + canvas.theme.port_spacing

have_audio_jack_in = have_midi_jack_in = have_midi_alsa_in = have_parameter_in = False
have_audio_jack_out = have_midi_jack_out = have_midi_alsa_out = have_parameter_out = False

# reset box size
self.p_width = 50
self.p_height = canvas.theme.box_header_height + canvas.theme.box_header_spacing + 1

# Check Text Name size
app_name_size = QFontMetrics(self.m_font_name).width(self.m_group_name) + 30
if app_name_size > self.p_width:
self.p_width = app_name_size
self.p_width = max( 50, app_name_size )

# Get Port List
port_list = []
@@ -2431,175 +2397,60 @@ class CanvasBox(QGraphicsItem):
if port.group_id == self.m_group_id and port.port_id in self.m_port_list_ids:
port_list.append(port)

# Get Max Box Width/Height
for port in port_list:
if port.port_mode == PORT_MODE_INPUT:
max_in_height += port_spacing

size = QFontMetrics(self.m_font_port).width(port.port_name)
if size > max_in_width:
max_in_width = size

if port.port_type == PORT_TYPE_AUDIO_JACK and not have_audio_jack_in:
have_audio_jack_in = True
max_in_height += canvas.theme.port_spacingT
elif port.port_type == PORT_TYPE_MIDI_JACK and not have_midi_jack_in:
have_midi_jack_in = True
max_in_height += canvas.theme.port_spacingT
elif port.port_type == PORT_TYPE_MIDI_ALSA and not have_midi_alsa_in:
have_midi_alsa_in = True
max_in_height += canvas.theme.port_spacingT
elif port.port_type == PORT_TYPE_PARAMETER and not have_parameter_in:
have_parameter_in = True
max_in_height += canvas.theme.port_spacingT

elif port.port_mode == PORT_MODE_OUTPUT:
max_out_height += port_spacing

size = QFontMetrics(self.m_font_port).width(port.port_name)
if size > max_out_width:
max_out_width = size

if port.port_type == PORT_TYPE_AUDIO_JACK and not have_audio_jack_out:
have_audio_jack_out = True
max_out_height += canvas.theme.port_spacingT
elif port.port_type == PORT_TYPE_MIDI_JACK and not have_midi_jack_out:
have_midi_jack_out = True
max_out_height += canvas.theme.port_spacingT
elif port.port_type == PORT_TYPE_MIDI_ALSA and not have_midi_alsa_out:
have_midi_alsa_out = True
max_out_height += canvas.theme.port_spacingT
elif port.port_type == PORT_TYPE_PARAMETER and not have_parameter_out:
have_parameter_out = True
max_out_height += canvas.theme.port_spacingT

if canvas.theme.port_spacingT == 0:
max_in_height += 2
max_out_height += 2

final_width = 30 + max_in_width + max_out_width
if final_width > self.p_width:
self.p_width = final_width

if max_in_height > self.p_height:
self.p_height = max_in_height

if max_out_height > self.p_height:
self.p_height = max_out_height

# Remove bottom space
self.p_height -= canvas.theme.port_spacingT

if canvas.theme.box_header_spacing > 0:
if len(port_list) == 0:
self.p_height -= canvas.theme.box_header_spacing
else:
self.p_height -= canvas.theme.box_header_spacing/2

last_in_pos = canvas.theme.box_header_height + canvas.theme.box_header_spacing
last_out_pos = canvas.theme.box_header_height + canvas.theme.box_header_spacing
last_in_type = PORT_TYPE_NULL
last_out_type = PORT_TYPE_NULL

# Re-position ports, AUDIO_JACK
for port in port_list:
if port.port_type != PORT_TYPE_AUDIO_JACK:
continue

if port.port_mode == PORT_MODE_INPUT:
if last_in_type != PORT_TYPE_NULL and port.port_type != last_in_type:
last_in_pos += canvas.theme.port_spacingT

port.widget.setPos(QPointF(1 + canvas.theme.port_offset, last_in_pos))
port.widget.setPortWidth(max_in_width)

last_in_pos += port_spacing
last_in_type = port.port_type

elif port.port_mode == PORT_MODE_OUTPUT:
if last_out_type != PORT_TYPE_NULL and port.port_type != last_out_type:
last_out_pos += canvas.theme.port_spacingT

port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 13, last_out_pos))
port.widget.setPortWidth(max_out_width)

last_out_pos += port_spacing
last_out_type = port.port_type

# Re-position ports, MIDI_JACK
for port in port_list:
if port.port_type != PORT_TYPE_MIDI_JACK:
continue

if port.port_mode == PORT_MODE_INPUT:
if last_in_type != PORT_TYPE_NULL and port.port_type != last_in_type:
last_in_pos += canvas.theme.port_spacingT

port.widget.setPos(QPointF(1 + canvas.theme.port_offset, last_in_pos))
port.widget.setPortWidth(max_in_width)

last_in_pos += port_spacing
last_in_type = port.port_type

elif port.port_mode == PORT_MODE_OUTPUT:
if last_out_type != PORT_TYPE_NULL and port.port_type != last_out_type:
last_out_pos += canvas.theme.port_spacingT

port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 13, last_out_pos))
port.widget.setPortWidth(max_out_width)

last_out_pos += port_spacing
last_out_type = port.port_type

# Re-position ports, MIDI_ALSA
for port in port_list:
if port.port_type != PORT_TYPE_MIDI_ALSA:
continue

if port.port_mode == PORT_MODE_INPUT:
if last_in_type != PORT_TYPE_NULL and port.port_type != last_in_type:
last_in_pos += canvas.theme.port_spacingT

port.widget.setPos(QPointF(1 + canvas.theme.port_offset, last_in_pos))
port.widget.setPortWidth(max_in_width)

last_in_pos += port_spacing
last_in_type = port.port_type

elif port.port_mode == PORT_MODE_OUTPUT:
if last_out_type != PORT_TYPE_NULL and port.port_type != last_out_type:
last_out_pos += canvas.theme.port_spacingT

port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 13, last_out_pos))
port.widget.setPortWidth(max_out_width)

last_out_pos += port_spacing
last_out_type = port.port_type

# Re-position ports, PARAMETER
for port in port_list:
if port.port_type != PORT_TYPE_PARAMETER:
continue

if port.port_mode == PORT_MODE_INPUT:
if last_in_type != PORT_TYPE_NULL and port.port_type != last_in_type:
last_in_pos += canvas.theme.port_spacingT

port.widget.setPos(QPointF(1 + canvas.theme.port_offset, last_in_pos))
port.widget.setPortWidth(max_in_width)

last_in_pos += port_spacing
last_in_type = port.port_type

elif port.port_mode == PORT_MODE_OUTPUT:
if last_out_type != PORT_TYPE_NULL and port.port_type != last_out_type:
last_out_pos += canvas.theme.port_spacingT

port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 13, last_out_pos))
port.widget.setPortWidth(max_out_width)

last_out_pos += port_spacing
last_out_type = port.port_type
if len(port_list) == 0:
self.p_height = canvas.theme.box_header_height
else:
max_in_width = max_out_width = 0
port_spacing = canvas.theme.port_height + canvas.theme.port_spacing

# Get Max Box Width, vertical ports re-positioning
port_types = [PORT_TYPE_AUDIO_JACK, PORT_TYPE_MIDI_JACK, PORT_TYPE_MIDI_ALSA, PORT_TYPE_PARAMETER]
last_in_type = last_out_type = PORT_TYPE_NULL
last_in_pos = last_out_pos = canvas.theme.box_header_height + canvas.theme.box_header_spacing

for port_type in port_types:
for port in port_list:
if port.port_type != port_type:
continue

size = QFontMetrics(self.m_font_port).width(port.port_name)

if port.port_mode == PORT_MODE_INPUT:
max_in_width = max( max_in_width, size )
if port.port_type != last_in_type:
if last_in_type != PORT_TYPE_NULL:
last_in_pos += canvas.theme.port_spacingT
last_in_type = port.port_type
port.widget.setY(last_in_pos)
last_in_pos += port_spacing

elif port.port_mode == PORT_MODE_OUTPUT:
max_out_width = max( max_out_width, size )
if port.port_type != last_out_type:
if last_out_type != PORT_TYPE_NULL:
last_out_pos += canvas.theme.port_spacingT
last_out_type = port.port_type
port.widget.setY(last_out_pos)
last_out_pos += port_spacing

self.p_width = max(self.p_width, 30 + max_in_width + max_out_width)

# Horizontal ports re-positioning
inX = 1 + canvas.theme.port_offset
outX = self.p_width - max_out_width - canvas.theme.port_offset - 13
for port_type in port_types:
for port in port_list:
if port.port_mode == PORT_MODE_INPUT:
port.widget.setX(inX)
port.widget.setPortWidth(max_in_width)

elif port.port_mode == PORT_MODE_OUTPUT:
port.widget.setX(outX)
port.widget.setPortWidth(max_out_width)

self.p_height = max(last_in_pos, last_out_pos)
self.p_height += max(canvas.theme.port_spacing, canvas.theme.port_spacingT) - canvas.theme.port_spacing
self.p_height += canvas.theme.box_pen.widthF()

self.repaintLines(True)
self.update()
@@ -2801,12 +2652,12 @@ class CanvasBox(QGraphicsItem):
def paint(self, painter, option, widget):
painter.save()
painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing == ANTIALIASING_FULL))
rect = QRectF(0, 0, self.p_width, self.p_height)

# Draw rectangle
if self.isSelected():
painter.setPen(canvas.theme.box_pen_sel)
else:
painter.setPen(canvas.theme.box_pen)
pen = canvas.theme.box_pen_sel if self.isSelected() else canvas.theme.box_pen
painter.setPen(pen)
lineHinting = pen.widthF() / 2

if canvas.theme.box_bg_type == Theme.THEME_BG_GRADIENT:
box_gradient = QLinearGradient(0, 0, 0, self.p_height)
@@ -2816,18 +2667,21 @@ class CanvasBox(QGraphicsItem):
else:
painter.setBrush(canvas.theme.box_bg_1)

lineHinting = painter.pen().widthF() / 2
painter.drawRect(QRectF(lineHinting, lineHinting, self.p_width - lineHinting*2, self.p_height - lineHinting*2))
rect.adjust(lineHinting, lineHinting, -lineHinting, -lineHinting)
painter.drawRect(rect)

# Draw pixmap header
rect.setHeight(canvas.theme.box_header_height)
if canvas.theme.box_header_pixmap:
painter.setPen(Qt.NoPen)
painter.setBrush(canvas.theme.box_bg_2)
painter.drawRect(1, 1, self.p_width-2, canvas.theme.box_header_height)

headerPos = QPointF(1, 1)
headerRect = QRectF(2, 2, self.p_width-4, canvas.theme.box_header_height-3)
painter.drawTiledPixmap(headerRect, canvas.theme.box_header_pixmap, headerPos)
# outline
rect.adjust(lineHinting, lineHinting, -lineHinting, -lineHinting)
painter.drawRect(rect)

rect.adjust(1, 1, -1, 0)
painter.drawTiledPixmap(rect, canvas.theme.box_header_pixmap, rect.topLeft())

# Draw text
painter.setFont(self.m_font_name)
@@ -2933,14 +2787,14 @@ class CanvasIcon(QGraphicsSvgItem):
return self.p_size

def paint(self, painter, option, widget):
if self.m_renderer:
painter.save()
painter.setRenderHint(QPainter.Antialiasing, False)
painter.setRenderHint(QPainter.TextAntialiasing, False)
self.m_renderer.render(painter, self.p_size)
painter.restore()
else:
QGraphicsSvgItem.paint(self, painter, option, widget)
if not self.m_renderer:
return QGraphicsSvgItem.paint(self, painter, option, widget)
painter.save()
painter.setRenderHint(QPainter.Antialiasing, False)
painter.setRenderHint(QPainter.TextAntialiasing, False)
self.m_renderer.render(painter, self.p_size)
painter.restore()

# ------------------------------------------------------------------------------
# canvasportglow.cpp


+ 8
- 8
source/patchcanvas_theme.py View File

@@ -118,9 +118,9 @@ class Theme(object):
self.port_parameter_text = self.port_text
self.port_parameter_text_sel = self.port_text

self.port_height = 15
self.port_height = 16
self.port_offset = 0
self.port_spacing = 3
self.port_spacing = 2
self.port_spacingT = 2

# Lines
@@ -200,9 +200,9 @@ class Theme(object):
self.port_parameter_text = self.port_text
self.port_parameter_text_sel = self.port_text

self.port_height = 11
self.port_height = 12
self.port_offset = 0
self.port_spacing = 2
self.port_spacing = 1
self.port_spacingT = 1

# Lines
@@ -282,9 +282,9 @@ class Theme(object):
self.port_parameter_text = self.port_text
self.port_parameter_text_sel = self.port_text

self.port_height = 15
self.port_height = 16
self.port_offset = 0
self.port_spacing = 3
self.port_spacing = 2
self.port_spacingT = 2

# Lines
@@ -450,9 +450,9 @@ class Theme(object):
self.port_parameter_text_sel = self.port_parameter_pen_sel

# missing, ports 2
self.port_height = 19
self.port_height = 21
self.port_offset = 1
self.port_spacing = 5
self.port_spacing = 3
self.port_spacingT = 0

# Lines


+ 29
- 14
source/plugin/Makefile View File

@@ -32,18 +32,10 @@ ifeq ($(HAVE_FLUIDSYNTH),true)
BUILD_CXX_FLAGS += $(FLUIDSYNTH_FLAGS)
endif

ifeq ($(HAVE_LINUXSAMPLER),true)
BUILD_CXX_FLAGS += $(LINUXSAMPLER_FLAGS)
endif

ifeq ($(HAVE_X11),true)
BUILD_CXX_FLAGS += $(X11_FLAGS)
endif

ifeq ($(CARLA_ZYN_COMPAT),true)
BUILD_CXX_FLAGS += -DCARLA_ZYN_LV2_EXPORTED
endif

# ----------------------------------------------------------------------------------------------------------------------------
# Libs

@@ -78,21 +70,34 @@ LINK_FLAGS += $(NATIVE_PLUGINS_LIBS)

LINK_FLAGS += $(FLUIDSYNTH_LIBS)
LINK_FLAGS += $(LIBLO_LIBS)
LINK_FLAGS += $(LINUXSAMPLER_LIBS)
LINK_FLAGS += $(MAGIC_LIBS)
LINK_FLAGS += $(X11_LIBS)

ifeq ($(MACOS),true)
# NOTE: this assumes only LV2 version will be built
SHARED += -Wl,-exported_symbol,_lv2_descriptor
SHARED += -Wl,-exported_symbol,_lv2ui_descriptor
SYMBOLS_LV2 = -Wl,-exported_symbol,_lv2_descriptor -Wl,-exported_symbol,_lv2ui_descriptor
SYMBOLS_LV2_UI = -Wl,-exported_symbol,_lv2ui_descriptor
SYMBOLS_VST = # TODO
endif

# ----------------------------------------------------------------------------------------------------------------------------
# ...

LIBS_ui = $(MODULEDIR)/water.a

# ----------------------------------------------------------------------------------------------------------------------------

TARGETS = \
$(BINDIR)/carla.lv2/carla$(LIB_EXT) \
$(BINDIR)/carla.lv2/carla$(LIB_EXT)

ifeq ($(HAVE_PYQT),true)
TARGETS += \
$(BINDIR)/carla.lv2/carla-ui$(LIB_EXT)
endif

ifneq ($(CROSS_COMPILING),true)
TARGETS += \
$(BINDIR)/carla.lv2/manifest.ttl
endif

ifeq ($(LINUX),true)
ifeq ($(HAVE_X11),true)
@@ -127,7 +132,12 @@ debug:
$(BINDIR)/carla.lv2/carla$(LIB_EXT): $(OBJDIR)/carla-lv2.cpp.o $(LIBS)
-@mkdir -p $(BINDIR)/carla.lv2
@echo "Linking carla.lv2/carla$(LIB_EXT)"
@$(CXX) $< $(LIBS_START) $(LIBS) $(LIBS_END) $(SHARED) $(LINK_FLAGS) -o $@
@$(CXX) $< $(LIBS_START) $(LIBS) $(LIBS_END) $(SHARED) $(SYMBOLS_LV2) $(LINK_FLAGS) -o $@

$(BINDIR)/carla.lv2/carla-ui$(LIB_EXT): $(OBJDIR)/carla-lv2-ui.cpp.o $(LIBS_ui)
-@mkdir -p $(BINDIR)/carla.lv2
@echo "Linking carla.lv2/carla-ui$(LIB_EXT)"
@$(CXX) $< $(LIBS_START) $(LIBS_ui) $(LIBS_END) $(SHARED) $(SYMBOLS_LV2_UI) $(LINK_FLAGS) -o $@

$(BINDIR)/CarlaRack$(LIB_EXT): $(OBJDIR)/carla-vst.cpp.rack-syn.o $(LIBS)
-@mkdir -p $(BINDIR)
@@ -166,6 +176,11 @@ $(OBJDIR)/carla-lv2.cpp.o: carla-lv2.cpp
@echo "Compiling $<"
@$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@

$(OBJDIR)/carla-lv2-ui.cpp.o: carla-lv2-ui.cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $<"
@$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@

$(OBJDIR)/carla-vst.cpp.rack-fx.o: carla-vst.cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $< (RackFX)"


+ 1
- 15
source/plugin/carla-base.cpp View File

@@ -76,21 +76,7 @@ struct PluginListManager {
std::strcmp(desc->label, "carlapatchbay16" ) == 0 ||
std::strcmp(desc->label, "carlapatchbay32" ) == 0 ||
std::strcmp(desc->label, "bigmeter" ) == 0 ||
std::strcmp(desc->label, "notes" ) == 0 ||
#ifdef CARLA_ZYN_LV2_EXPORTED
std::strcmp(desc->label, "zynalienwah" ) == 0 ||
std::strcmp(desc->label, "zynchorus" ) == 0 ||
std::strcmp(desc->label, "zyndistortion" ) == 0 ||
std::strcmp(desc->label, "zyndynamicfilter") == 0 ||
std::strcmp(desc->label, "zynecho" ) == 0 ||
std::strcmp(desc->label, "zynphaser" ) == 0 ||
std::strcmp(desc->label, "zynreverb" ) == 0 ||
std::strcmp(desc->label, "zynaddsubfx" ) == 0 ||
#endif
std::strcmp(desc->label, "at1" ) == 0 ||
std::strcmp(desc->label, "bls1" ) == 0 ||
std::strcmp(desc->label, "rev1-ambisonic" ) == 0 ||
std::strcmp(desc->label, "rev1-stereo" ) == 0)
std::strcmp(desc->label, "notes" ) == 0)
{
descs.append(desc);
}


+ 85
- 14
source/plugin/carla-lv2-export.cpp View File

@@ -1,6 +1,6 @@
/*
* Carla Native Plugins
* Copyright (C) 2013-2017 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2013-2018 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -30,6 +30,7 @@
#include "lv2/ui.h"
#include "lv2/units.h"
#include "lv2/urid.h"
#include "lv2/worker.h"
#include "lv2/lv2_external_ui.h"
#include "lv2/lv2_programs.h"

@@ -66,13 +67,14 @@ static const String nameToSymbol(const String& name, const uint32_t portIndex)
}
else
{
if (std::isdigit(trimmedName[0]))
symbol += "_";

for (int i=0; i < trimmedName.length(); ++i)
{
const water_uchar c = trimmedName[i];

if (i == 0 && std::isdigit(c))
symbol += "_";
else if (std::isalpha(c) || std::isdigit(c))
if (std::isalpha(c) || std::isdigit(c))
symbol += c;
else
symbol += "_";
@@ -135,7 +137,8 @@ static void writeManifestFile(PluginListManager& plm)
// -------------------------------------------------------------------
// UI

#ifdef CARLA_OS_LINUX
#ifdef HAVE_PYQT
# if defined(CARLA_OS_LINUX) && defined(HAVE_X11)
text += "<http://kxstudio.sf.net/carla/ui-embed>\n";
text += " a <" LV2_UI__X11UI "> ;\n";
text += " ui:binary <carla" PLUGIN_EXT "> ;\n";
@@ -146,7 +149,7 @@ static void writeManifestFile(PluginListManager& plm)
text += " <" LV2_UI__resize "> ;\n";
text += " opts:supportedOption <" LV2_PARAMETERS__sampleRate "> .\n";
text += "\n";
#endif
# endif

text += "<http://kxstudio.sf.net/carla/ui-ext>\n";
text += " a <" LV2_EXTERNAL_UI__Widget "> ;\n";
@@ -156,6 +159,15 @@ static void writeManifestFile(PluginListManager& plm)
text += " <" LV2_PROGRAMS__UIInterface "> ;\n";
text += " lv2:requiredFeature <" LV2_INSTANCE_ACCESS_URI "> ;\n";
text += " opts:supportedOption <" LV2_PARAMETERS__sampleRate "> .\n";
text += "\n";

text += "<http://kxstudio.sf.net/carla/ui-bridge-ext>\n";
text += " a <" LV2_EXTERNAL_UI__Widget "> ;\n";
text += " ui:binary <carla-ui" PLUGIN_EXT "> ;\n";
text += " lv2:extensionData <" LV2_UI__idleInterface "> ,\n";
text += " <" LV2_UI__showInterface "> ,\n";
text += " <" LV2_PROGRAMS__UIInterface "> .\n";
#endif

// -------------------------------------------------------------------
// Write file now
@@ -292,6 +304,9 @@ static void writePluginFile(const NativePluginDescriptor* const pluginDesc)
if (pluginDesc->category != NATIVE_PLUGIN_CATEGORY_SYNTH)
text += " lv2:extensionData <" LV2_PROGRAMS__Interface "> ;\n";

if (pluginDesc->hints & NATIVE_PLUGIN_HAS_UI)
text += " lv2:extensionData <" LV2_WORKER__interface "> ;\n";

text += "\n";

// -------------------------------------------------------------------
@@ -305,46 +320,56 @@ static void writePluginFile(const NativePluginDescriptor* const pluginDesc)
// -------------------------------------------------------------------
// UIs

#ifdef HAVE_PYQT
if (pluginDesc->hints & NATIVE_PLUGIN_HAS_UI)
{
#ifdef CARLA_OS_LINUX
# if defined(CARLA_OS_LINUX) && defined(HAVE_X11)
if (std::strncmp(pluginDesc->label, "carla", 5) == 0)
{
text += " ui:ui <http://kxstudio.sf.net/carla/ui-embed> ,\n";
text += " <http://kxstudio.sf.net/carla/ui-ext> ;\n";
text += " <http://kxstudio.sf.net/carla/ui-ext> ,\n";
text += " <http://kxstudio.sf.net/carla/ui-bridge-ext> ;\n";
}
else
#endif
# endif
{
text += " ui:ui <http://kxstudio.sf.net/carla/ui-ext> ;\n";
text += " ui:ui <http://kxstudio.sf.net/carla/ui-ext> ,\n";
text += " <http://kxstudio.sf.net/carla/ui-bridge-ext> ;\n";
}

text += "\n";
}
#endif

// -------------------------------------------------------------------
// First MIDI/Time port
// First input MIDI/Time/UI port

if (pluginDesc->midiIns > 0 || (pluginDesc->hints & NATIVE_PLUGIN_USES_TIME) != 0)
const bool hasEventInPort = (pluginDesc->hints & NATIVE_PLUGIN_USES_TIME) != 0 || (pluginDesc->hints & NATIVE_PLUGIN_HAS_UI) != 0;

if (pluginDesc->midiIns > 0 || hasEventInPort)
{
text += " lv2:port [\n";
text += " a lv2:InputPort, atom:AtomPort ;\n";
text += " atom:bufferType atom:Sequence ;\n";

if (pluginDesc->midiIns > 0 && (pluginDesc->hints & NATIVE_PLUGIN_USES_TIME) != 0)
if (pluginDesc->midiIns > 0 && hasEventInPort)
{
text += " atom:supports <" LV2_MIDI__MidiEvent "> ,\n";
text += " <" LV2_TIME__Position "> ;\n";
}
else if (pluginDesc->midiIns > 0)
{
text += " atom:supports <" LV2_MIDI__MidiEvent "> ;\n";
}
else
{
text += " atom:supports <" LV2_TIME__Position "> ;\n";
}

text += " lv2:designation lv2:control ;\n";
text += " lv2:index " + String(portIndex++) + " ;\n";

if (pluginDesc->hints & NATIVE_PLUGIN_USES_TIME)
if (hasEventInPort)
{
if (pluginDesc->midiIns > 1)
{
@@ -395,6 +420,52 @@ static void writePluginFile(const NativePluginDescriptor* const pluginDesc)
text += " ] , [\n";
}

// -------------------------------------------------------------------
// First output MIDI/UI port

const bool hasEventOutPort = (pluginDesc->hints & NATIVE_PLUGIN_HAS_UI) != 0;

if (pluginDesc->midiIns > 0 || hasEventOutPort)
{
text += " lv2:port [\n";
text += " a lv2:OutputPort, atom:AtomPort ;\n";
text += " atom:bufferType atom:Sequence ;\n";

if (pluginDesc->midiOuts > 0)
text += " atom:supports <" LV2_MIDI__MidiEvent "> ;\n";

text += " lv2:index " + String(portIndex++) + " ;\n";

if (hasEventOutPort)
{
if (pluginDesc->midiOuts > 1)
{
text += " lv2:symbol \"lv2_events_out_1\" ;\n";
text += " lv2:name \"Events Input #1\" ;\n";
}
else
{
text += " lv2:symbol \"lv2_events_out\" ;\n";
text += " lv2:name \"Events Output\" ;\n";
}
}
else
{
if (pluginDesc->midiOuts > 1)
{
text += " lv2:symbol \"lv2_midi_out_" + String(portIndex+1) + "\" ;\n";
text += " lv2:name \"MIDI Output #" + String(portIndex+1) + "\" ;\n";
}
else
{
text += " lv2:symbol \"lv2_midi_out\" ;\n";
text += " lv2:name \"MIDI Output\" ;\n";
}
}

text += " ] ;\n\n";
}

// -------------------------------------------------------------------
// MIDI outputs



+ 440
- 0
source/plugin/carla-lv2-ui.cpp View File

@@ -0,0 +1,440 @@
/*
* Carla Native Plugins
* Copyright (C) 2013-2018 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

#ifndef HAVE_PYQT
# error This file should not be built
#endif

#include "CarlaLv2Utils.hpp"
#include "CarlaPipeUtils.hpp"

// --------------------------------------------------------------------------------------------------------------------

class NativePluginUI : public LV2_External_UI_Widget_Compat
{
public:
NativePluginUI(LV2UI_Write_Function writeFunction, LV2UI_Controller controller,
LV2UI_Widget* widget, const LV2_Feature* const* features)
: fUridMap(nullptr),
fUridUnmap(nullptr),
fUridTranser(0),
fUridTranser2(0),
fUI()
{
run = extui_run;
show = extui_show;
hide = extui_hide;

fUI.writeFunction = writeFunction;
fUI.controller = controller;

const LV2_URID_Map* uridMap = nullptr;
const LV2_URID_Unmap* uridUnmap = nullptr;

for (int i=0; features[i] != nullptr; ++i)
{
/**/ if (std::strcmp(features[i]->URI, LV2_URID__map) == 0)
uridMap = (const LV2_URID_Map*)features[i]->data;
else if (std::strcmp(features[i]->URI, LV2_URID__unmap) == 0)
uridUnmap = (const LV2_URID_Unmap*)features[i]->data;
}

if (uridMap == nullptr)
{
carla_stderr("Host doesn't provide urid-map feature");
return;
}

fUridMap = uridMap;
fUridUnmap = uridUnmap;
fUridTranser = uridMap->map(uridMap->handle, LV2_ATOM__eventTransfer);
fUridTranser2 = uridMap->map(uridMap->handle, "urn:carla:transmitEv");

// ---------------------------------------------------------------
// see if the host supports external-ui

for (int i=0; features[i] != nullptr; ++i)
{
if (std::strcmp(features[i]->URI, LV2_EXTERNAL_UI__Host) == 0 ||
std::strcmp(features[i]->URI, LV2_EXTERNAL_UI_DEPRECATED_URI) == 0)
{
fUI.host = (const LV2_External_UI_Host*)features[i]->data;
break;
}
}

if (fUI.host != nullptr)
{
fUI.name = carla_strdup(fUI.host->plugin_human_id);
*widget = (LV2_External_UI_Widget_Compat*)this;
return;
}

// ---------------------------------------------------------------
// no external-ui support, use showInterface

for (int i=0; features[i] != nullptr; ++i)
{
if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) != 0)
continue;

const LV2_Options_Option* const options((const LV2_Options_Option*)features[i]->data);
CARLA_SAFE_ASSERT_BREAK(options != nullptr);

for (int j=0; options[j].key != 0; ++j)
{
if (options[j].key != uridMap->map(uridMap->handle, LV2_UI__windowTitle))
continue;

const char* const title((const char*)options[j].value);
CARLA_SAFE_ASSERT_BREAK(title != nullptr && title[0] != '\0');

fUI.name = carla_strdup(title);
break;
}
break;
}

if (fUI.name == nullptr)
fUI.name = carla_strdup("Carla");

*widget = nullptr;
}

~NativePluginUI()
{
if (fUI.isVisible)
writeAtomMessage("quit");

fUI.host = nullptr;
fUI.writeFunction = nullptr;
fUI.controller = nullptr;
}

// ----------------------------------------------------------------------------------------------------------------

void lv2ui_port_event(uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer)
{
CARLA_SAFE_ASSERT_RETURN(buffer != nullptr,);

if (format == 0)
{
char msg[128];
const float* const valuePtr = (const float*)buffer;

{
const ScopedLocale csl;
std::snprintf(msg, 127, "control %u %f", portIndex, *valuePtr);
}

msg[127] = '\0';

writeAtomMessage(msg);
return;
}

if (format == fUridTranser)
{
CARLA_SAFE_ASSERT_RETURN(bufferSize > sizeof(LV2_Atom),);

const LV2_Atom* const atom = (const LV2_Atom*)buffer;

if (atom->type == fUridTranser2)
{
const char* const msg = (const char*)(atom + 1);

if (std::strcmp(msg, "quit") == 0)
{
handleUiClosed();
}

return;
}
}

if (fUridUnmap != nullptr)
{
carla_stdout("lv2ui_port_event %u %u %u:%s %p",
portIndex, bufferSize, format, fUridUnmap->unmap(fUridUnmap->handle, format), buffer);
}
}

// ----------------------------------------------------------------------------------------------------------------

void lv2ui_select_program(uint32_t bank, uint32_t program) const
{
char msg[128];
std::snprintf(msg, 127, "program %u %u", bank, program);
msg[127] = '\0';

writeAtomMessage(msg);
}

// ----------------------------------------------------------------------------------------------------------------

int lv2ui_idle() const
{
if (! fUI.isVisible)
return 1;

handleUiRun();
return 0;
}

int lv2ui_show()
{
handleUiShow();
return 0;
}

int lv2ui_hide()
{
handleUiHide();
return 0;
}

// ----------------------------------------------------------------------------------------------------------------

protected:
void handleUiShow()
{
writeAtomMessage("show");
fUI.isVisible = true;
}

void handleUiHide()
{
if (fUI.isVisible)
{
fUI.isVisible = false;
writeAtomMessage("hide");
}
}

void handleUiRun() const
{
if (fUI.isVisible)
writeAtomMessage("idle");
}

void handleUiClosed()
{
fUI.isVisible = false;

if (fUI.host != nullptr && fUI.host->ui_closed != nullptr && fUI.controller != nullptr)
fUI.host->ui_closed(fUI.controller);

fUI.host = nullptr;
fUI.writeFunction = nullptr;
fUI.controller = nullptr;
}

bool writeAtomMessage(const char* const msg) const
{
CARLA_SAFE_ASSERT_RETURN(fUI.writeFunction != nullptr, false);
CARLA_SAFE_ASSERT_RETURN(fUridTranser2 != 0, false);
carla_debug("writeAtomMessage(%s)", msg);

const size_t msgSize = std::strlen(msg)+1;
const size_t atomSize = sizeof(LV2_Atom) + msgSize;

if (atomSize <= 128)
{
char atomBuf[atomSize];
carla_zeroChars(atomBuf, atomSize);

LV2_Atom* const atom = (LV2_Atom*)atomBuf;
atom->size = msgSize;
atom->type = fUridTranser2;
std::memcpy(atomBuf+sizeof(LV2_Atom), msg, msgSize);

fUI.writeFunction(fUI.controller, 0, atomSize, fUridTranser, atomBuf);
}
else
{
char* const atomBuf = new char[atomSize];
carla_zeroChars(atomBuf, atomSize);

LV2_Atom* const atom = (LV2_Atom*)atomBuf;
atom->size = msgSize;
atom->type = fUridTranser2;
std::memcpy(atomBuf+sizeof(LV2_Atom), msg, msgSize);

fUI.writeFunction(fUI.controller, 0, atomSize, fUridTranser, atomBuf);

delete[] atomBuf;
}

return true;
}

// ----------------------------------------------------------------------------------------------------------------

private:
const LV2_URID_Map* fUridMap;
const LV2_URID_Unmap* fUridUnmap;
LV2_URID fUridTranser, fUridTranser2;

struct UI {
const LV2_External_UI_Host* host;
LV2UI_Write_Function writeFunction;
LV2UI_Controller controller;
const char* name;
bool isVisible;

UI()
: host(nullptr),
writeFunction(nullptr),
controller(nullptr),
name(nullptr),
isVisible(false) {}

~UI()
{
if (name != nullptr)
{
delete[] name;
name = nullptr;
}
}
} fUI;

// ----------------------------------------------------------------------------------------------------------------

#define handlePtr ((NativePluginUI*)handle)

static void extui_run(LV2_External_UI_Widget_Compat* handle)
{
handlePtr->handleUiRun();
}

static void extui_show(LV2_External_UI_Widget_Compat* handle)
{
carla_debug("extui_show(%p)", handle);
handlePtr->handleUiShow();
}

static void extui_hide(LV2_External_UI_Widget_Compat* handle)
{
carla_debug("extui_hide(%p)", handle);
handlePtr->handleUiHide();
}

#undef handlePtr

// -------------------------------------------------------------------

CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NativePluginUI)
};

// -----------------------------------------------------------------------
// LV2 UI descriptor functions

static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char*, const char*,
LV2UI_Write_Function writeFunction, LV2UI_Controller controller,
LV2UI_Widget* widget, const LV2_Feature* const* features)
{
carla_debug("lv2ui_instantiate(..., %p, %p, %p)", writeFunction, controller, widget, features);

NativePluginUI* const ui = new NativePluginUI(writeFunction, controller, widget, features);

// TODO: check ok

return (LV2UI_Handle)ui;
}

#define uiPtr ((NativePluginUI*)ui)

static void lv2ui_port_event(LV2UI_Handle ui, uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer)
{
carla_debug("lv2ui_port_event(%p, %i, %i, %i, %p)", ui, portIndex, bufferSize, format, buffer);
uiPtr->lv2ui_port_event(portIndex, bufferSize, format, buffer);
}

static void lv2ui_cleanup(LV2UI_Handle ui)
{
carla_debug("lv2ui_cleanup(%p)", ui);
delete uiPtr;
}

static void lv2ui_select_program(LV2UI_Handle ui, uint32_t bank, uint32_t program)
{
carla_debug("lv2ui_select_program(%p, %i, %i)", ui, bank, program);
uiPtr->lv2ui_select_program(bank, program);
}

static int lv2ui_idle(LV2UI_Handle ui)
{
return uiPtr->lv2ui_idle();
}

static int lv2ui_show(LV2UI_Handle ui)
{
carla_debug("lv2ui_show(%p)", ui);
return uiPtr->lv2ui_show();
}

static int lv2ui_hide(LV2UI_Handle ui)
{
carla_debug("lv2ui_hide(%p)", ui);
return uiPtr->lv2ui_hide();
}

static const void* lv2ui_extension_data(const char* uri)
{
carla_stdout("lv2ui_extension_data(\"%s\")", uri);

static const LV2UI_Idle_Interface uiidle = { lv2ui_idle };
static const LV2UI_Show_Interface uishow = { lv2ui_show, lv2ui_hide };
static const LV2_Programs_UI_Interface uiprograms = { lv2ui_select_program };

if (std::strcmp(uri, LV2_UI__idleInterface) == 0)
return &uiidle;
if (std::strcmp(uri, LV2_UI__showInterface) == 0)
return &uishow;
if (std::strcmp(uri, LV2_PROGRAMS__UIInterface) == 0)
return &uiprograms;

return nullptr;
}

#undef uiPtr

// -----------------------------------------------------------------------
// Startup code

CARLA_EXPORT
const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
{
carla_debug("lv2ui_descriptor(%i)", index);

static const LV2UI_Descriptor lv2UiExtDesc = {
/* URI */ "http://kxstudio.sf.net/carla/ui-bridge-ext",
/* instantiate */ lv2ui_instantiate,
/* cleanup */ lv2ui_cleanup,
/* port_event */ lv2ui_port_event,
/* extension_data */ lv2ui_extension_data
};

return (index == 0) ? &lv2UiExtDesc : nullptr;
}

// -----------------------------------------------------------------------

#include "CarlaPipeUtils.cpp"

// -----------------------------------------------------------------------

+ 128
- 14
source/plugin/carla-lv2.cpp View File

@@ -34,14 +34,15 @@ public:
const double sampleRate,
const char* const bundlePath,
const LV2_Feature* const* const features)
: Lv2PluginBaseClass(sampleRate, features),
: Lv2PluginBaseClass<NativeTimeInfo>(sampleRate, features),
fHandle(nullptr),
fHost(),
fDescriptor(desc),
#ifdef CARLA_PROPER_CPP11_SUPPORT
fProgramDesc({0, 0, nullptr}),
#endif
fMidiEventCount(0)
fMidiEventCount(0),
fWorkerUISignal(0)
{
carla_zeroStruct(fHost);

@@ -104,6 +105,7 @@ public:
fHandle = fDescriptor->instantiate(&fHost);
CARLA_SAFE_ASSERT_RETURN(fHandle != nullptr, false);

fPorts.hasUI = fDescriptor->hints & NATIVE_PLUGIN_HAS_UI;
fPorts.usesTime = fDescriptor->hints & NATIVE_PLUGIN_USES_TIME;
fPorts.numAudioIns = fDescriptor->audioIns;
fPorts.numAudioOuts = fDescriptor->audioOuts;
@@ -198,6 +200,25 @@ public:
{
if (event == nullptr)
continue;

if (event->body.type == fURIs.uiEvents && fWorkerUISignal != -1)
{
if (fWorker != nullptr)
{
// worker is supported by the host, we can continue
fWorkerUISignal = 1;
const char* const msg((const char*)(event + 1));
const size_t msgSize = std::strlen(msg);
fWorker->schedule_work(fWorker->handle, msgSize+1, msg);
}
else
{
// worker is not supported, cancel
fWorkerUISignal = -1;
}
continue;
}

if (event->body.type != fURIs.midiEvent)
continue;
if (event->body.size > 4)
@@ -230,6 +251,31 @@ public:
const_cast<float**>(fPorts.audioIns), fPorts.audioOuts, frames,
fMidiEvents, fMidiEventCount);

if (fWorkerUISignal == -1 && fPorts.hasUI)
{
const char* const msg = "quit";
const size_t msgSize = 5;

LV2_Atom_Sequence* const seq(fPorts.eventsOut[0]);
Ports::EventsOutData& mData(fPorts.eventsOutData[0]);

if (sizeof(LV2_Atom_Event) + msgSize <= mData.capacity - mData.offset)
{
LV2_Atom_Event* const aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, seq) + mData.offset);

aev->time.frames = 0;
aev->body.size = msgSize;
aev->body.type = fURIs.uiEvents;
std::memcpy(LV2_ATOM_BODY(&aev->body), msg, msgSize);

const uint32_t size = lv2_atom_pad_size(static_cast<uint32_t>(sizeof(LV2_Atom_Event) + msgSize));
mData.offset += size;
seq->atom.size += size;

fWorkerUISignal = 0;
}
}

lv2_post_run(frames);
updateParameterOutputs();
}
@@ -321,6 +367,42 @@ public:

// ----------------------------------------------------------------------------------------------------------------

LV2_Worker_Status lv2_work(LV2_Worker_Respond_Function, LV2_Worker_Respond_Handle, uint32_t, const void* data)
{
const char* const msg = (const char*)data;

/**/ if (std::strcmp(msg, "show") == 0)
{
handleUiShow();
}
else if (std::strcmp(msg, "hide") == 0)
{
handleUiHide();
}
else if (std::strcmp(msg, "idle") == 0)
{
handleUiRun();
}
else if (std::strcmp(msg, "quit") == 0)
{
handleUiRun();
}
else
{
carla_stdout("lv2_work unknown msg '%s'", msg);
return LV2_WORKER_ERR_UNKNOWN;
}

return LV2_WORKER_SUCCESS;
}

LV2_Worker_Status lv2_work_resp(uint32_t /*size*/, const void* /*body*/)
{
return LV2_WORKER_SUCCESS;
}

// ----------------------------------------------------------------------------------------------------------------

void lv2ui_instantiate(LV2UI_Write_Function writeFunction, LV2UI_Controller controller,
LV2UI_Widget* widget, const LV2_Feature* const* features, const bool isEmbed)
{
@@ -334,7 +416,7 @@ public:
fHost.uiName = nullptr;
}

#ifdef CARLA_OS_LINUX
#if defined(CARLA_OS_LINUX) && defined(HAVE_X11)
// ---------------------------------------------------------------
// show embed UI if needed

@@ -437,6 +519,10 @@ public:
fHost.uiName = carla_strdup(fDescriptor->name);

*widget = nullptr;
return;

// maybe be unused
(void)isEmbed;
}

void lv2ui_port_event(uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer) const
@@ -523,10 +609,10 @@ protected:
const uint8_t port(event->port);
CARLA_SAFE_ASSERT_RETURN(port < fPorts.numMidiOuts, false);

LV2_Atom_Sequence* const seq(fPorts.midiOuts[port]);
LV2_Atom_Sequence* const seq(fPorts.eventsOut[port]);
CARLA_SAFE_ASSERT_RETURN(seq != nullptr, false);

Ports::MidiOutData& mData(fPorts.midiOutData[port]);
Ports::EventsOutData& mData(fPorts.eventsOutData[port]);

if (sizeof(LV2_Atom_Event) + event->size > mData.capacity - mData.offset)
return false;
@@ -547,8 +633,11 @@ protected:

void handleUiParameterChanged(const uint32_t index, const float value) const
{
if (fUI.writeFunction != nullptr && fUI.controller != nullptr)
fUI.writeFunction(fUI.controller, index+fPorts.indexOffset, sizeof(float), 0, &value);
if (fWorkerUISignal)
{
}
else if (fUI.writeFunction != nullptr && fUI.controller != nullptr)
fUI.writeFunction(fUI.controller, index+fPorts.indexOffset, sizeof(float), 0, &value);
}

void handleUiCustomDataChanged(const char* const /*key*/, const char* const /*value*/) const
@@ -558,13 +647,17 @@ protected:

void handleUiClosed()
{
fUI.isVisible = false;

if (fWorkerUISignal)
fWorkerUISignal = -1;

if (fUI.host != nullptr && fUI.host->ui_closed != nullptr && fUI.controller != nullptr)
fUI.host->ui_closed(fUI.controller);

fUI.host = nullptr;
fUI.writeFunction = nullptr;
fUI.controller = nullptr;
fUI.isVisible = false;
}

const char* handleUiOpenFile(const bool /*isDir*/, const char* const /*title*/, const char* const /*filter*/) const
@@ -639,6 +732,8 @@ private:
uint32_t fMidiEventCount;
NativeMidiEvent fMidiEvents[kMaxMidiEvents];

int fWorkerUISignal;

// -------------------------------------------------------------------

#define handlePtr ((NativePlugin*)handle)
@@ -823,6 +918,18 @@ static LV2_State_Status lv2_restore(LV2_Handle instance, LV2_State_Retrieve_Func
return instancePtr->lv2_restore(retrieve, handle, flags, features);
}

static LV2_Worker_Status lv2_work(LV2_Handle instance, LV2_Worker_Respond_Function respond, LV2_Worker_Respond_Handle handle, uint32_t size, const void* data)
{
carla_debug("work(%p, %p, %p, %u, %p)", instance, respond, handle, size, data);
return instancePtr->lv2_work(respond, handle, size, data);
}

LV2_Worker_Status lv2_work_resp(LV2_Handle instance, uint32_t size, const void* body)
{
carla_debug("work_resp(%p, %u, %p)", instance, size, body);
return instancePtr->lv2_work_resp(size, body);
}

static const void* lv2_extension_data(const char* uri)
{
carla_debug("lv2_extension_data(\"%s\")", uri);
@@ -830,6 +937,7 @@ static const void* lv2_extension_data(const char* uri)
static const LV2_Options_Interface options = { lv2_get_options, lv2_set_options };
static const LV2_Programs_Interface programs = { lv2_get_program, lv2_select_program };
static const LV2_State_Interface state = { lv2_save, lv2_restore };
static const LV2_Worker_Interface worker = { lv2_work, lv2_work_resp, nullptr };

if (std::strcmp(uri, LV2_OPTIONS__interface) == 0)
return &options;
@@ -837,12 +945,15 @@ static const void* lv2_extension_data(const char* uri)
return &programs;
if (std::strcmp(uri, LV2_STATE__interface) == 0)
return &state;
if (std::strcmp(uri, LV2_WORKER__interface) == 0)
return &worker;

return nullptr;
}

#undef instancePtr

#ifdef HAVE_PYQT
// -----------------------------------------------------------------------
// LV2 UI descriptor functions

@@ -850,9 +961,9 @@ static LV2UI_Handle lv2ui_instantiate(LV2UI_Write_Function writeFunction, LV2UI_
LV2UI_Widget* widget, const LV2_Feature* const* features, const bool isEmbed)
{
carla_debug("lv2ui_instantiate(..., %p, %p, %p)", writeFunction, controller, widget, features);
#ifndef CARLA_OS_LINUX
# if defined(CARLA_OS_LINUX) && defined(HAVE_X11)
CARLA_SAFE_ASSERT_RETURN(! isEmbed, nullptr);
#endif
# endif

NativePlugin* plugin = nullptr;

@@ -876,14 +987,14 @@ static LV2UI_Handle lv2ui_instantiate(LV2UI_Write_Function writeFunction, LV2UI_
return (LV2UI_Handle)plugin;
}

#ifdef CARLA_OS_LINUX
# if defined(CARLA_OS_LINUX) && defined(HAVE_X11)
static LV2UI_Handle lv2ui_instantiate_embed(const LV2UI_Descriptor*, const char*, const char*,
LV2UI_Write_Function writeFunction, LV2UI_Controller controller,
LV2UI_Widget* widget, const LV2_Feature* const* features)
{
return lv2ui_instantiate(writeFunction, controller, widget, features, true);
}
#endif
# endif

static LV2UI_Handle lv2ui_instantiate_external(const LV2UI_Descriptor*, const char*, const char*,
LV2UI_Write_Function writeFunction, LV2UI_Controller controller,
@@ -896,7 +1007,7 @@ static LV2UI_Handle lv2ui_instantiate_external(const LV2UI_Descriptor*, const ch

static void lv2ui_port_event(LV2UI_Handle ui, uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer)
{
carla_debug("lv2ui_port_event(%p, %i, %i, %i, %p)", ui, portIndex, bufferSize, format, buffer);
carla_debug("lv2ui_port_eventxx(%p, %i, %i, %i, %p)", ui, portIndex, bufferSize, format, buffer);
uiPtr->lv2ui_port_event(portIndex, bufferSize, format, buffer);
}

@@ -946,6 +1057,7 @@ static const void* lv2ui_extension_data(const char* uri)

return nullptr;
}
#endif

#undef uiPtr

@@ -1002,12 +1114,13 @@ const LV2_Descriptor* lv2_descriptor(uint32_t index)
return lv2Desc;
}

#ifdef HAVE_PYQT
CARLA_EXPORT
const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
{
carla_debug("lv2ui_descriptor(%i)", index);

#ifdef CARLA_OS_LINUX
#if defined(CARLA_OS_LINUX) && defined(HAVE_X11)
static const LV2UI_Descriptor lv2UiEmbedDesc = {
/* URI */ "http://kxstudio.sf.net/carla/ui-embed",
/* instantiate */ lv2ui_instantiate_embed,
@@ -1032,5 +1145,6 @@ const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)

return (index == 0) ? &lv2UiExtDesc : nullptr;
}
#endif

// -----------------------------------------------------------------------

+ 63
- 0
source/rest/Makefile View File

@@ -0,0 +1,63 @@
#!/usr/bin/make -f
# Makefile for carla-interposer #
# ----------------------------- #
# Created by falkTX
#

CWD=..
include $(CWD)/Makefile.mk

# ----------------------------------------------------------------------------------------------------------------------

BINDIR := $(CWD)/../bin

ifeq ($(DEBUG),true)
OBJDIR := $(CWD)/../build/interposer/Debug
MODULEDIR := $(CWD)/../build/modules/Debug
else
OBJDIR := $(CWD)/../build/interposer/Release
MODULEDIR := $(CWD)/../build/modules/Release
endif

# ----------------------------------------------------------------------------------------------------------------------

BUILD_CXX_FLAGS += -I$(CWD) -I$(CWD)/backend -I$(CWD)/includes -I$(CWD)/modules -I$(CWD)/utils

LINK_FLAGS += -L$(BINDIR) -lcarla_standalone2 -lcarla_utils -lrestbed -lpthread -Wl,-rpath=$(shell realpath $(CWD)/../bin)

# ----------------------------------------------------------------------------------------------------------------------

OBJS = $(OBJDIR)/rest-server.cpp.o $(OBJDIR)/buffers.cpp.o
TARGETS = $(BINDIR)/carla-rest-server

# ----------------------------------------------------------------------------------------------------------------------

all: $(TARGETS)

# ----------------------------------------------------------------------------------------------------------------------

clean:
rm -f $(OBJDIR)/*.o $(TARGETS)

debug:
$(MAKE) DEBUG=true

# ----------------------------------------------------------------------------------------------------------------------

$(BINDIR)/carla-rest-server: $(OBJS)
-@mkdir -p $(BINDIR)
@echo "Linking carla-rest-server"
@$(CXX) $^ $(LINK_FLAGS) -o $@

# ----------------------------------------------------------------------------------------------------------------------

$(OBJDIR)/%.cpp.o: %.cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $<"
@$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@

# ----------------------------------------------------------------------------------------------------------------------

-include $(OBJS:%.o=%.d)

# ----------------------------------------------------------------------------------------------------------------------

+ 312
- 0
source/rest/buffers.cpp View File

@@ -0,0 +1,312 @@
/*
* Carla REST API Server
* Copyright (C) 2018 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

#include "buffers.hpp"
#include "CarlaMathUtils.hpp"

#include <cstdio>
#include <cstring>

// -------------------------------------------------------------------------------------------------------------------

enum {
kJsonBufSize = 4095,
kStrBufSize = 1023,
kSizeBufSize = 31,
};

// static buffer to return json
// NOTE size is never checked for json, the buffer is big enough in order to assume it all always fits
static char jsonBuf[kJsonBufSize+1];

// static buffer to return size
static char sizeBuf[kSizeBufSize+1];

// static buffer to return regular strings
static char strBuf[kStrBufSize+1];

// -------------------------------------------------------------------------------------------------------------------

const char* size_buf(const char* const buf)
{
const std::size_t size = std::strlen(buf);
std::snprintf(sizeBuf, kSizeBufSize, P_SIZE, size);
sizeBuf[kSizeBufSize] = '\0';
return sizeBuf;
}

// -------------------------------------------------------------------------------------------------------------------

const char* str_buf_bool(const bool value)
{
strBuf[0] = value ? '1' : '0';
strBuf[1] = '\0';
return strBuf;
}

// -------------------------------------------------------------------------------------------------------------------

const char* str_buf_float(const double value)
{
std::snprintf(strBuf, kStrBufSize, "%f", value);
strBuf[kStrBufSize] = '\0';
return strBuf;
}

const char* str_buf_float_array(const double* const values, const char sep)
{
std::size_t bytesRead = 0;
char tmpBuf[32];

for (int i=0; carla_isNotZero(values[i]) && bytesRead < kStrBufSize; ++i)
{
std::snprintf(tmpBuf, 31, "%f", values[i]);
tmpBuf[31] = '\0';

const std::size_t size = std::strlen(tmpBuf);

if (bytesRead + size > kStrBufSize)
break;

std::strncpy(strBuf+bytesRead, tmpBuf, kStrBufSize - bytesRead);
bytesRead += size;
strBuf[bytesRead] = sep;
bytesRead += 1;
}

strBuf[bytesRead > 0 ? bytesRead-1 : 0] = '\0';
return strBuf;
}

// -------------------------------------------------------------------------------------------------------------------

const char* str_buf_string(const char* const string)
{
std::strncpy(strBuf, string, kStrBufSize);
strBuf[kStrBufSize] = '\0';
return strBuf;
}

const char* str_buf_string_array(const char* const* const array)
{
std::size_t bytesRead = 0;

for (int i=0; array[i] != nullptr && bytesRead < kStrBufSize; ++i)
{
const std::size_t size = std::strlen(array[i]);

if (bytesRead + size > kStrBufSize)
break;

std::strncpy(strBuf+bytesRead, array[i], kStrBufSize - bytesRead);
bytesRead += size;
strBuf[bytesRead] = '\n';
bytesRead += 1;
}

strBuf[bytesRead > 0 ? bytesRead-1 : 0] = '\0';
return strBuf;
}

const char* str_buf_string_quoted(const char* const string)
{
const std::size_t size = std::strlen(string);
char* strBufPtr = strBuf;

*strBufPtr++ = '"';

for (std::size_t i=0, bytesWritten=0; i < size && bytesWritten < kStrBufSize-1; ++i)
{
switch (string[i])
{
case '"':
case '\\':
*strBufPtr++ = '\\';
++bytesWritten;
break;
case '\n':
*strBufPtr++ = '\\';
*strBufPtr++ = 'n';;
bytesWritten += 2;
continue;
case '\f':
*strBufPtr++ = '\\';
*strBufPtr++ = 'f';;
bytesWritten += 2;
continue;
}

*strBufPtr++ = string[i];
++bytesWritten;
}

*strBufPtr++ = '"';
*strBufPtr++ = '\0';
return strBuf;
}

// -------------------------------------------------------------------------------------------------------------------

const char* str_buf_int(const int value)
{
std::snprintf(strBuf, kStrBufSize, "%i", value);
strBuf[kStrBufSize] = '\0';
return strBuf;
}

const char* str_buf_int64(const int64_t value)
{
std::snprintf(strBuf, kStrBufSize, P_INT64, value);
strBuf[kStrBufSize] = '\0';
return strBuf;
}

const char* str_buf_uint(const uint value)
{
std::snprintf(strBuf, kStrBufSize, "%u", value);
strBuf[kStrBufSize] = '\0';
return strBuf;
}

const char* str_buf_uint64(const uint64_t value)
{
std::snprintf(strBuf, kStrBufSize, P_UINT64, value);
strBuf[kStrBufSize] = '\0';
return strBuf;
}

const char* str_buf_uint_array(const uint* const values, const char sep)
{
std::size_t bytesRead = 0;
char tmpBuf[32];

for (int i=0; values[i] != 0 && bytesRead < kStrBufSize; ++i)
{
std::snprintf(tmpBuf, 31, "%u", values[i]);
tmpBuf[31] = '\0';

const std::size_t size = std::strlen(tmpBuf);

if (bytesRead + size > kStrBufSize)
break;

std::strncpy(strBuf+bytesRead, tmpBuf, kStrBufSize - bytesRead);
bytesRead += size;
strBuf[bytesRead] = sep;
bytesRead += 1;
}

strBuf[bytesRead > 0 ? bytesRead-1 : 0] = '\0';
return strBuf;
}

// -------------------------------------------------------------------------------------------------------------------

char* json_buf_start()
{
std::strcpy(jsonBuf, "{");
return jsonBuf + 1;
}

char* json_buf_add(char* jsonBufPtr, const char* const key, const char* const valueBuf)
{
if (jsonBufPtr != jsonBuf+1)
*jsonBufPtr++ = ',';

*jsonBufPtr++ = '"';

std::strcpy(jsonBufPtr, key);
jsonBufPtr += std::strlen(key);

*jsonBufPtr++ = '"';
*jsonBufPtr++ = ':';

std::strcpy(jsonBufPtr, valueBuf);
jsonBufPtr += std::strlen(valueBuf);

return jsonBufPtr;
}

template <typename T, typename Fn>
char* json_buf_add_fn(char* jsonBufPtr, const char* const key, const T value, const Fn fn)
{
return json_buf_add(jsonBufPtr, key, fn(value));
}

template <typename T, typename Fn>
char* json_buf_add_fn_array(char* jsonBufPtr, const char* const key, const T value, const Fn fn)
{
return json_buf_add(jsonBufPtr, key, fn(value, ','));
}

// -------------------------------------------------------------------------------------------------------------------

char* json_buf_add_bool(char* jsonBufPtr, const char* const key, const bool value)
{
static const char* const kTrue = "true";
static const char* const kFalse = "false";
return json_buf_add_fn(jsonBufPtr, key, value ? kTrue : kFalse, str_buf_string);
}

char* json_buf_add_float(char* jsonBufPtr, const char* const key, const double value)
{
return json_buf_add_fn(jsonBufPtr, key, value, str_buf_float);
}

char* json_buf_add_float_array(char* jsonBufPtr, const char* const key, const double* const values)
{
return json_buf_add_fn_array(jsonBufPtr, key, values, str_buf_float_array);
}

char* json_buf_add_string(char* jsonBufPtr, const char* const key, const char* const value)
{
return json_buf_add_fn(jsonBufPtr, key, value, str_buf_string_quoted);
}

char* json_buf_add_int(char* jsonBufPtr, const char* const key, const int value)
{
return json_buf_add_fn(jsonBufPtr, key, value, str_buf_int);
}

char* json_buf_add_int64(char* jsonBufPtr, const char* const key, const int64_t value)
{
return json_buf_add_fn(jsonBufPtr, key, value, str_buf_int64);
}

char* json_buf_add_uint(char* jsonBufPtr, const char* const key, const uint value)
{
return json_buf_add_fn(jsonBufPtr, key, value, str_buf_uint);
}

char* json_buf_add_uint_array(char* jsonBufPtr, const char* const key, const uint* const values)
{
return json_buf_add_fn_array(jsonBufPtr, key, values, str_buf_uint_array);
}

char* json_buf_add_uint64(char* jsonBufPtr, const char* const key, const uint64_t value)
{
return json_buf_add_fn(jsonBufPtr, key, value, str_buf_uint64);
}

const char* json_buf_end(char* jsonBufPtr)
{
*jsonBufPtr++ = '}';
*jsonBufPtr++ = '\0';
return jsonBuf;
}

// -------------------------------------------------------------------------------------------------------------------

+ 58
- 0
source/rest/buffers.hpp View File

@@ -0,0 +1,58 @@
/*
* Carla REST API Server
* Copyright (C) 2018 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

#ifndef REST_BUFFERS_HPP_INCLUDED
#define REST_BUFFERS_HPP_INCLUDED

#include "CarlaDefines.h"

#ifdef CARLA_PROPER_CPP11_SUPPORT
# include <cstdint>
#else
# include <stdint.h>
#endif

// size buf
const char* size_buf(const char* const buf);

// base types
const char* str_buf_bool(const bool value);
const char* str_buf_float(const double value);
const char* str_buf_float_array(const double* const values, const char sep = '\n');
const char* str_buf_string(const char* const string);
const char* str_buf_string_array(const char* const* const array);
const char* str_buf_string_quoted(const char* const string);
const char* str_buf_int(const int value);
const char* str_buf_int64(const int64_t value);
const char* str_buf_uint(const uint value);
const char* str_buf_uint64(const uint64_t value);
const char* str_buf_uint_array(const uint* const values, const char sep = '\n');

// json
char* json_buf_start();
char* json_buf_add_bool(char* jsonBufPtr, const char* const key, const bool value);
char* json_buf_add_float(char* jsonBufPtr, const char* const key, const double value);
char* json_buf_add_float_array(char* jsonBufPtr, const char* const key, const double* const values);
char* json_buf_add_string(char* jsonBufPtr, const char* const key, const char* const value);
char* json_buf_add_int(char* jsonBufPtr, const char* const key, const int value);
char* json_buf_add_int64(char* jsonBufPtr, const char* const key, const int64_t value);
char* json_buf_add_uint(char* jsonBufPtr, const char* const key, const uint value);
char* json_buf_add_uint64(char* jsonBufPtr, const char* const key, const uint64_t value);
char* json_buf_add_uint_array(char* jsonBufPtr, const char* const key, const uint* const values);
const char* json_buf_end(char* jsonBufPtr);

#endif // REST_BUFFERS_HPP_INCLUDED

+ 1228
- 0
source/rest/carla-host.cpp
File diff suppressed because it is too large
View File


+ 89
- 0
source/rest/carla-utils.cpp View File

@@ -0,0 +1,89 @@
/*
* Carla REST API Server
* Copyright (C) 2018 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

#include "common.hpp"

#include "CarlaUtils.h"

// -------------------------------------------------------------------------------------------------------------------

void handle_carla_get_complete_license_text(const std::shared_ptr<Session> session)
{
const char* const buf = str_buf_string(carla_get_complete_license_text());
session->close(OK, buf, { { "Content-Length", size_buf(buf) } } );
}

void handle_carla_get_supported_file_extensions(const std::shared_ptr<Session> session)
{
const char* const buf = str_buf_string_array(carla_get_supported_file_extensions());
session->close(OK, buf, { { "Content-Length", size_buf(buf) } } );
}

void handle_carla_get_supported_features(const std::shared_ptr<Session> session)
{
const char* const buf = str_buf_string_array(carla_get_supported_features());
session->close(OK, buf, { { "Content-Length", size_buf(buf) } } );
}

void handle_carla_get_cached_plugin_count(const std::shared_ptr<Session> session)
{
const std::shared_ptr<const Request> request = session->get_request();

const int ptype = std::atoi(request->get_query_parameter("ptype").c_str());
CARLA_SAFE_ASSERT_RETURN(ptype >= PLUGIN_NONE && ptype <= PLUGIN_JACK,)

const std::string pluginPath = request->get_query_parameter("pluginPath");

const char* const buf = str_buf_uint(carla_get_cached_plugin_count(static_cast<PluginType>(ptype),
pluginPath.c_str()));
session->close(OK, buf, { { "Content-Length", size_buf(buf) } } );
}

void handle_carla_get_cached_plugin_info(const std::shared_ptr<Session> session)
{
const std::shared_ptr<const Request> request = session->get_request();

const int ptype = std::atoi(request->get_query_parameter("ptype").c_str());
CARLA_SAFE_ASSERT_RETURN(ptype >= PLUGIN_NONE && ptype <= PLUGIN_JACK,)

const int index = std::atoi(request->get_query_parameter("index").c_str());
CARLA_SAFE_ASSERT_RETURN(index >= 0 /*&& index < INT_MAX*/,)

const CarlaCachedPluginInfo* const info = carla_get_cached_plugin_info(static_cast<PluginType>(ptype),
static_cast<uint>(index));

char* jsonBuf;
jsonBuf = json_buf_start();
jsonBuf = json_buf_add_bool(jsonBuf, "valid", info->valid);
jsonBuf = json_buf_add_uint(jsonBuf, "category", info->category);
jsonBuf = json_buf_add_uint(jsonBuf, "hints", info->hints);
jsonBuf = json_buf_add_uint(jsonBuf, "audioIns", info->audioIns);
jsonBuf = json_buf_add_uint(jsonBuf, "audioOuts", info->audioOuts);
jsonBuf = json_buf_add_uint(jsonBuf, "midiIns", info->midiIns);
jsonBuf = json_buf_add_uint(jsonBuf, "midiOuts", info->midiOuts);
jsonBuf = json_buf_add_uint(jsonBuf, "parameterIns", info->parameterIns);
jsonBuf = json_buf_add_uint(jsonBuf, "parameterOuts", info->parameterOuts);
jsonBuf = json_buf_add_string(jsonBuf, "name", info->name);
jsonBuf = json_buf_add_string(jsonBuf, "label", info->label);
jsonBuf = json_buf_add_string(jsonBuf, "maker", info->maker);
jsonBuf = json_buf_add_string(jsonBuf, "copyright", info->copyright);

const char* const buf = json_buf_end(jsonBuf);
session->close(OK, buf, { { "Content-Length", size_buf(buf) } } );
}

// -------------------------------------------------------------------------------------------------------------------

+ 40
- 0
source/rest/common.hpp View File

@@ -0,0 +1,40 @@
/*
* Carla REST API Server
* Copyright (C) 2018 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

#ifndef REST_COMMON_HPP_INCLUDED
#define REST_COMMON_HPP_INCLUDED

#include "buffers.hpp"

#include "CarlaBackend.h"
#include "CarlaUtils.hpp"

#include <restbed>

using restbed::Request;
using restbed::Resource;
using restbed::Service;
using restbed::Session;
using restbed::Settings;
using restbed::BAD_REQUEST;
using restbed::OK;

CARLA_BACKEND_USE_NAMESPACE;

void send_server_side_message(const char* const message);

#endif // REST_COMMON_HPP_INCLUDED

+ 247
- 0
source/rest/rest-server.cpp View File

@@ -0,0 +1,247 @@
/*
* Carla REST API Server
* Copyright (C) 2018 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

/* NOTE
* Even though Carla is GPL, restbed if AGPL.
* As such, the resulting binary will be AGPL.
* Take this into consideration before deploying it to any servers.
*/

#include "common.hpp"

#include "carla-host.cpp"
#include "carla-utils.cpp"

#include "CarlaMutex.hpp"
#include "CarlaStringList.hpp"

// -------------------------------------------------------------------------------------------------------------------

std::vector<std::shared_ptr<Session>> gSessions;

CarlaStringList gSessionMessages;
CarlaMutex gSessionMessagesMutex;

// -------------------------------------------------------------------------------------------------------------------

void send_server_side_message(const char* const message)
{
const CarlaMutexLocker cml(gSessionMessagesMutex);

gSessionMessages.append(message);
}

// -------------------------------------------------------------------------------------------------------------------

static void register_server_side_handler(const std::shared_ptr<Session> session)
{
const auto headers = std::multimap<std::string, std::string> {
{ "Connection", "keep-alive" },
{ "Cache-Control", "no-cache" },
{ "Content-Type", "text/event-stream" },
{ "Access-Control-Allow-Origin", "*" } //Only required for demo purposes.
};

session->yield(OK, headers, [](const std::shared_ptr<Session> rsession) {
gSessions.push_back(rsession);
});
}

static void event_stream_handler(void)
{
static bool firstInit = true;

if (firstInit)
{
firstInit = false;
carla_stdout("Carla REST-API Server started");
}

gSessions.erase(
std::remove_if(gSessions.begin(), gSessions.end(),
[](const std::shared_ptr<Session> &a) {
return a->is_closed();
}),
gSessions.end());

CarlaStringList messages;

{
const CarlaMutexLocker cml(gSessionMessagesMutex);

if (gSessionMessages.count() > 0)
gSessionMessages.moveTo(messages);
}

for (auto message : messages)
{
// std::puts(message);

for (auto session : gSessions)
session->yield(OK, message);
}
}

// -------------------------------------------------------------------------------------------------------------------

static void make_resource(Service& service,
const char* const path,
const std::function<void (const std::shared_ptr<Session>)>& callback)
{
std::shared_ptr<Resource> resource = std::make_shared<Resource>();
resource->set_path(path);
resource->set_method_handler("GET", callback);
service.publish(resource);
}

// -------------------------------------------------------------------------------------------------------------------

int main(int, const char**)
{
Service service;

// server-side messages
{
std::shared_ptr<Resource> resource = std::make_shared<Resource>();
resource->set_path("/stream");
resource->set_method_handler("GET", register_server_side_handler);
service.publish(resource);
}

// carla-host
make_resource(service, "/get_engine_driver_count", handle_carla_get_engine_driver_count);
make_resource(service, "/get_engine_driver_name", handle_carla_get_engine_driver_name);
make_resource(service, "/get_engine_driver_device_names", handle_carla_get_engine_driver_device_names);
make_resource(service, "/get_engine_driver_device_info", handle_carla_get_engine_driver_device_info);

make_resource(service, "/engine_init", handle_carla_engine_init);
make_resource(service, "/engine_close", handle_carla_engine_close);
make_resource(service, "/is_engine_running", handle_carla_is_engine_running);
make_resource(service, "/set_engine_about_to_close", handle_carla_set_engine_about_to_close);

make_resource(service, "/set_engine_option", handle_carla_set_engine_option);
make_resource(service, "/load_file", handle_carla_load_file);
make_resource(service, "/load_project", handle_carla_load_project);
make_resource(service, "/save_project", handle_carla_save_project);

make_resource(service, "/patchbay_connect", handle_carla_patchbay_connect);
make_resource(service, "/patchbay_disconnect", handle_carla_patchbay_disconnect);
make_resource(service, "/patchbay_refresh", handle_carla_patchbay_refresh);

make_resource(service, "/transport_play", handle_carla_transport_play);
make_resource(service, "/transport_pause", handle_carla_transport_pause);
make_resource(service, "/transport_bpm", handle_carla_transport_bpm);
make_resource(service, "/transport_relocate", handle_carla_transport_relocate);
make_resource(service, "/get_current_transport_frame", handle_carla_get_current_transport_frame);
make_resource(service, "/get_transport_info", handle_carla_get_transport_info);

make_resource(service, "/get_current_plugin_count", handle_carla_get_current_plugin_count);
make_resource(service, "/get_max_plugin_number", handle_carla_get_max_plugin_number);
make_resource(service, "/add_plugin", handle_carla_add_plugin);
make_resource(service, "/remove_plugin", handle_carla_remove_plugin);
make_resource(service, "/remove_all_plugins", handle_carla_remove_all_plugins);

make_resource(service, "/rename_plugin", handle_carla_rename_plugin);
make_resource(service, "/clone_plugin", handle_carla_clone_plugin);
make_resource(service, "/replace_plugin", handle_carla_replace_plugin);
make_resource(service, "/switch_plugins", handle_carla_switch_plugins);

make_resource(service, "/load_plugin_state", handle_carla_load_plugin_state);
make_resource(service, "/save_plugin_state", handle_carla_save_plugin_state);
make_resource(service, "/export_plugin_lv2", handle_carla_export_plugin_lv2);

make_resource(service, "/get_plugin_info", handle_carla_get_plugin_info);
make_resource(service, "/get_audio_port_count_info", handle_carla_get_audio_port_count_info);
make_resource(service, "/get_midi_port_count_info", handle_carla_get_midi_port_count_info);
make_resource(service, "/get_parameter_count_info", handle_carla_get_parameter_count_info);
make_resource(service, "/get_parameter_info", handle_carla_get_parameter_info);
make_resource(service, "/get_parameter_scalepoint_info", handle_carla_get_parameter_scalepoint_info);

make_resource(service, "/get_parameter_data", handle_carla_get_parameter_data);
make_resource(service, "/get_parameter_ranges", handle_carla_get_parameter_ranges);
make_resource(service, "/get_midi_program_data", handle_carla_get_midi_program_data);
make_resource(service, "/get_custom_data", handle_carla_get_custom_data);
make_resource(service, "/get_custom_data_value", handle_carla_get_custom_data_value);
make_resource(service, "/get_chunk_data", handle_carla_get_chunk_data);

make_resource(service, "/get_parameter_count", handle_carla_get_parameter_count);
make_resource(service, "/get_program_count", handle_carla_get_program_count);
make_resource(service, "/get_midi_program_count", handle_carla_get_midi_program_count);
make_resource(service, "/get_custom_data_count", handle_carla_get_custom_data_count);

make_resource(service, "/get_parameter_text", handle_carla_get_parameter_text);
make_resource(service, "/get_program_name", handle_carla_get_program_name);
make_resource(service, "/get_midi_program_name", handle_carla_get_midi_program_name);
make_resource(service, "/get_real_plugin_name", handle_carla_get_real_plugin_name);

make_resource(service, "/get_current_program_index", handle_carla_get_current_program_index);
make_resource(service, "/get_current_midi_program_index", handle_carla_get_current_midi_program_index);

make_resource(service, "/get_default_parameter_value", handle_carla_get_default_parameter_value);
make_resource(service, "/get_current_parameter_value", handle_carla_get_current_parameter_value);
make_resource(service, "/get_internal_parameter_value", handle_carla_get_internal_parameter_value);
make_resource(service, "/get_input_peak_value", handle_carla_get_input_peak_value);
make_resource(service, "/get_output_peak_value", handle_carla_get_output_peak_value);

make_resource(service, "/set_active", handle_carla_set_active);
make_resource(service, "/set_drywet", handle_carla_set_drywet);
make_resource(service, "/set_volume", handle_carla_set_volume);
make_resource(service, "/set_balance_left", handle_carla_set_balance_left);
make_resource(service, "/set_balance_right", handle_carla_set_balance_right);
make_resource(service, "/set_panning", handle_carla_set_panning);
make_resource(service, "/set_ctrl_channel", handle_carla_set_ctrl_channel);
make_resource(service, "/set_option", handle_carla_set_option);

make_resource(service, "/set_parameter_value", handle_carla_set_parameter_value);
make_resource(service, "/set_parameter_midi_channel", handle_carla_set_parameter_midi_channel);
make_resource(service, "/set_parameter_midi_cc", handle_carla_set_parameter_midi_cc);
make_resource(service, "/set_program", handle_carla_set_program);
make_resource(service, "/set_midi_program", handle_carla_set_midi_program);
make_resource(service, "/set_custom_data", handle_carla_set_custom_data);
make_resource(service, "/set_chunk_data", handle_carla_set_chunk_data);

make_resource(service, "/prepare_for_save", handle_carla_prepare_for_save);
make_resource(service, "/reset_parameters", handle_carla_reset_parameters);
make_resource(service, "/randomize_parameters", handle_carla_randomize_parameters);
make_resource(service, "/send_midi_note", handle_carla_send_midi_note);

make_resource(service, "/get_buffer_size", handle_carla_get_buffer_size);
make_resource(service, "/get_sample_rate", handle_carla_get_sample_rate);
make_resource(service, "/get_last_error", handle_carla_get_last_error);
make_resource(service, "/get_host_osc_url_tcp", handle_carla_get_host_osc_url_tcp);
make_resource(service, "/get_host_osc_url_udp", handle_carla_get_host_osc_url_udp);

// carla-utils
make_resource(service, "/get_complete_license_text", handle_carla_get_complete_license_text);
make_resource(service, "/get_supported_file_extensions", handle_carla_get_supported_file_extensions);
make_resource(service, "/get_supported_features", handle_carla_get_supported_features);
make_resource(service, "/get_cached_plugin_count", handle_carla_get_cached_plugin_count);
make_resource(service, "/get_cached_plugin_info", handle_carla_get_cached_plugin_info);

// schedule events
service.schedule(engine_idle_handler); // FIXME, crashes on fast times, but we need ~30Hz for OSC..
service.schedule(event_stream_handler, std::chrono::milliseconds(500));

std::shared_ptr<Settings> settings = std::make_shared<Settings>();
settings->set_port(2228);
settings->set_default_header("Connection", "close");

service.start(settings);
return 0;
}

// -------------------------------------------------------------------------------------------------------------------

+ 4
- 4
source/utils/CarlaBackendUtils.hpp View File

@@ -163,7 +163,7 @@ const char* InternalParameterIndex2Str(const InternalParameterIndex index) noexc
{
case PARAMETER_NULL:
return "PARAMETER_NULL";
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
case PARAMETER_ACTIVE:
return "PARAMETER_ACTIVE";
case PARAMETER_DRYWET:
@@ -206,7 +206,7 @@ const char* EngineCallbackOpcode2Str(const EngineCallbackOpcode opcode) noexcept
return "ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED";
case ENGINE_CALLBACK_PARAMETER_DEFAULT_CHANGED:
return "ENGINE_CALLBACK_PARAMETER_DEFAULT_CHANGED";
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
case ENGINE_CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED:
return "ENGINE_CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED";
case ENGINE_CALLBACK_PARAMETER_MIDI_CC_CHANGED:
@@ -234,7 +234,7 @@ const char* EngineCallbackOpcode2Str(const EngineCallbackOpcode opcode) noexcept
return "ENGINE_CALLBACK_RELOAD_PROGRAMS";
case ENGINE_CALLBACK_RELOAD_ALL:
return "ENGINE_CALLBACK_RELOAD_ALL";
#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
case ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED:
return "ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED";
case ENGINE_CALLBACK_PATCHBAY_CLIENT_REMOVED:
@@ -325,7 +325,7 @@ const char* EngineOption2Str(const EngineOption option) noexcept
return "ENGINE_OPTION_PREVENT_BAD_BEHAVIOUR";
case ENGINE_OPTION_FRONTEND_WIN_ID:
return "ENGINE_OPTION_FRONTEND_WIN_ID";
#ifndef CARLA_OS_WIN
#if !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH) && !defined(CARLA_OS_WIN)
case ENGINE_OPTION_WINE_EXECUTABLE:
return "ENGINE_OPTION_WINE_EXECUTABLE";
case ENGINE_OPTION_WINE_AUTO_PREFIX:


+ 23
- 0
source/utils/CarlaBinaryUtils.hpp View File

@@ -25,6 +25,9 @@

#ifdef HAVE_LIBMAGIC
# include <magic.h>
# ifdef CARLA_OS_MAC
# include "CarlaMacUtils.hpp"
# endif
#endif

CARLA_BACKEND_START_NAMESPACE
@@ -95,6 +98,26 @@ BinaryType getBinaryTypeFromFile(const char* const filename)
? BINARY_POSIX64
: BINARY_POSIX32;

# ifdef CARLA_OS_MAC
if (std::strcmp(output, "directory") == 0)
if (const char* const binary = findBinaryInBundle(filename))
return getBinaryTypeFromFile(binary);

if (std::strstr(output, "Mach-O universal binary") != nullptr)
{
// This is tricky, binary actually contains multiple architectures
// 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"))
return BINARY_POSIX32;
if (std::strstr(output, "ppc"))
return BINARY_OTHER;
}

carla_stdout("getBinaryTypeFromFile(\"%s\") - have output:\n%s", filename, output);
# endif

return BINARY_NATIVE;
}
#endif


+ 4
- 3
source/utils/CarlaBridgeDefines.hpp View File

@@ -20,7 +20,7 @@

#include "CarlaRingBuffer.hpp"

#define CARLA_PLUGIN_BRIDGE_API_VERSION 4
#define CARLA_PLUGIN_BRIDGE_API_VERSION 5

// -------------------------------------------------------------------------------------------------------------------

@@ -37,7 +37,7 @@ enum PluginBridgeRtClientOpcode {
kPluginBridgeRtClientControlEventAllSoundOff, // uint/frame, byte/chan
kPluginBridgeRtClientControlEventAllNotesOff, // uint/frame, byte/chan
kPluginBridgeRtClientMidiEvent, // uint/frame, byte/port, byte/size, byte[]/data
kPluginBridgeRtClientProcess,
kPluginBridgeRtClientProcess, // uint/frames
kPluginBridgeRtClientQuit
};

@@ -134,12 +134,13 @@ struct BridgeSemaphore {
};
};

// needs to be 64bit aligned
// NOTE: needs to be 64bit aligned
struct BridgeTimeInfo {
uint64_t playing;
uint64_t frame;
uint64_t usecs;
uint32_t validFlags;
uint32_t unused;
// bbt
int32_t bar, beat;
float beatsPerBar, beatType;


+ 3
- 0
source/utils/CarlaLibCounter.hpp View File

@@ -104,6 +104,9 @@ public:
lib.filename = dfilename;
lib.count = 1;
lib.canDelete = canDelete;
#ifdef BUILD_BRIDGE
lib.canDelete = true;
#endif

if (fLibs.append(lib))
return libPtr;


+ 57
- 29
source/utils/CarlaLv2Utils.hpp View File

@@ -554,6 +554,7 @@ public:
fBufferSize(0),
fSampleRate(sampleRate),
fUridMap(nullptr),
fWorker(nullptr),
fTimeInfo(),
fLastPositionData(),
fURIs(),
@@ -572,20 +573,23 @@ public:
const LV2_Options_Option* options = nullptr;
const LV2_URID_Map* uridMap = nullptr;
const LV2_URID_Unmap* uridUnmap = nullptr;
const LV2_Worker_Schedule* worker = nullptr;

for (int i=0; features[i] != nullptr; ++i)
{
if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0)
/**/ if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0)
options = (const LV2_Options_Option*)features[i]->data;
else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0)
uridMap = (const LV2_URID_Map*)features[i]->data;
else if (std::strcmp(features[i]->URI, LV2_URID__unmap) == 0)
uridUnmap = (const LV2_URID_Unmap*)features[i]->data;
else if (std::strcmp(features[i]->URI, LV2_WORKER__schedule) == 0)
worker = (const LV2_Worker_Schedule*)features[i]->data;
}

if (options == nullptr || uridMap == nullptr)
{
carla_stderr("Host doesn't provide option or urid-map features");
carla_stderr("Host doesn't provide option and urid-map features");
return;
}

@@ -640,6 +644,8 @@ public:
fUridMap = uridMap;
fURIs.map(uridMap);

fWorker = worker;

carla_zeroStruct(fTimeInfo);
carla_zeroStruct(fLastPositionData);
}
@@ -929,15 +935,15 @@ public:
return false;

// init midi out data
if (fPorts.numMidiOuts > 0)
if (fPorts.numMidiOuts > 0 || fPorts.hasUI)
{
for (uint32_t i=0; i<fPorts.numMidiOuts; ++i)
{
LV2_Atom_Sequence* const seq(fPorts.midiOuts[i]);
LV2_Atom_Sequence* const seq(fPorts.eventsOut[i]);
CARLA_SAFE_ASSERT_CONTINUE(seq != nullptr);

fPorts.midiOutData[i].capacity = seq->atom.size;
fPorts.midiOutData[i].offset = 0;
fPorts.eventsOutData[i].capacity = seq->atom.size;
fPorts.eventsOutData[i].offset = 0;

seq->atom.size = sizeof(LV2_Atom_Sequence_Body);
seq->atom.type = fURIs.atomSequence;
@@ -1140,6 +1146,7 @@ protected:

// LV2 host features
const LV2_URID_Map* fUridMap;
const LV2_Worker_Schedule* fWorker;

// Time info stuff
TimeInfoStruct fTimeInfo;
@@ -1184,11 +1191,11 @@ protected:
// Port stuff
struct Ports {
// need to save current state
struct MidiOutData {
struct EventsOutData {
uint32_t capacity;
uint32_t offset;

MidiOutData()
EventsOutData()
: capacity(0),
offset(0) {}
};
@@ -1200,12 +1207,13 @@ protected:
uint32_t numMidiIns;
uint32_t numMidiOuts;
uint32_t numParams;
bool hasUI;
bool usesTime;

// port buffers
const LV2_Atom_Sequence** eventsIn;
/* */ LV2_Atom_Sequence** midiOuts;
/* */ MidiOutData* midiOutData;
/* */ LV2_Atom_Sequence** eventsOut;
/* */ EventsOutData* eventsOutData;
const float** audioIns;
/* */ float** audioOuts;
/* */ float* freewheel;
@@ -1222,10 +1230,11 @@ protected:
numMidiIns(0),
numMidiOuts(0),
numParams(0),
hasUI(false),
usesTime(false),
eventsIn(nullptr),
midiOuts(nullptr),
midiOutData(nullptr),
eventsOut(nullptr),
eventsOutData(nullptr),
audioIns(nullptr),
audioOuts(nullptr),
freewheel(nullptr),
@@ -1241,16 +1250,16 @@ protected:
eventsIn = nullptr;
}

if (midiOuts != nullptr)
if (eventsOut != nullptr)
{
delete[] midiOuts;
midiOuts = nullptr;
delete[] eventsOut;
eventsOut = nullptr;
}

if (midiOutData != nullptr)
if (eventsOutData != nullptr)
{
delete[] midiOutData;
midiOutData = nullptr;
delete[] eventsOutData;
eventsOutData = nullptr;
}

if (audioIns != nullptr)
@@ -1294,7 +1303,7 @@ protected:
for (uint32_t i=0; i < numMidiIns; ++i)
eventsIn[i] = nullptr;
}
else if (usesTime)
else if (usesTime || hasUI)
{
eventsIn = new const LV2_Atom_Sequence*[1];
eventsIn[0] = nullptr;
@@ -1302,11 +1311,16 @@ protected:

if (numMidiOuts > 0)
{
midiOuts = new LV2_Atom_Sequence*[numMidiOuts];
midiOutData = new MidiOutData[numMidiOuts];
eventsOut = new LV2_Atom_Sequence*[numMidiOuts];
eventsOutData = new EventsOutData[numMidiOuts];

for (uint32_t i=0; i < numMidiOuts; ++i)
midiOuts[i] = nullptr;
eventsOut[i] = nullptr;
}
else if (hasUI)
{
eventsOut = new LV2_Atom_Sequence*[1];
eventsOut[0] = nullptr;
}

if (numAudioIns > 0)
@@ -1338,9 +1352,11 @@ protected:
// NOTE: need to be filled in by the parent class
}

indexOffset = numAudioIns + numAudioOuts + numMidiOuts;
// 1 event port for time if no midi input is used
indexOffset += numMidiIns > 0 ? numMidiIns : (usesTime ? 1 : 0);
indexOffset = numAudioIns + numAudioOuts;
// 1 event port for time or ui if no midi input is used
indexOffset += numMidiIns > 0 ? numMidiIns : ((usesTime || hasUI) ? 1 : 0);
// 1 event port for ui if no midi output is used
indexOffset += numMidiOuts > 0 ? numMidiOuts : (hasUI ? 1 : 0);
// 1 extra for freewheel port
indexOffset += 1;
}
@@ -1349,7 +1365,7 @@ protected:
{
uint32_t index = 0;

if (numMidiIns > 0 || usesTime)
if (numMidiIns > 0 || usesTime || hasUI)
{
if (port == index++)
{
@@ -1367,11 +1383,20 @@ protected:
}
}

for (uint32_t i=0; i < numMidiOuts; ++i)
if (numMidiOuts > 0 || hasUI)
{
if (port == index++)
{
eventsOut[0] = (LV2_Atom_Sequence*)dataLocation;
return;
}
}

for (uint32_t i=1; i < numMidiOuts; ++i)
{
if (port == index++)
{
midiOuts[i] = (LV2_Atom_Sequence*)dataLocation;
eventsOut[i] = (LV2_Atom_Sequence*)dataLocation;
return;
}
}
@@ -1433,6 +1458,7 @@ protected:
LV2_URID timeFrame;
LV2_URID timeSpeed;
LV2_URID timeTicksPerBeat;
LV2_URID uiEvents;

URIDs()
: atomBlank(0),
@@ -1452,7 +1478,8 @@ protected:
timeBeatUnit(0),
timeFrame(0),
timeSpeed(0),
timeTicksPerBeat(0) {}
timeTicksPerBeat(0),
uiEvents(0) {}

void map(const LV2_URID_Map* const uridMap)
{
@@ -1474,6 +1501,7 @@ protected:
timeBeatsPerBar = uridMap->map(uridMap->handle, LV2_TIME__beatsPerBar);
timeBeatsPerMinute = uridMap->map(uridMap->handle, LV2_TIME__beatsPerMinute);
timeTicksPerBeat = uridMap->map(uridMap->handle, LV2_KXSTUDIO_PROPERTIES__TimePositionTicksPerBeat);
uiEvents = uridMap->map(uridMap->handle, "urn:carla:transmitEv");
}
} fURIs;



+ 72
- 0
source/utils/CarlaMacUtils.cpp View File

@@ -0,0 +1,72 @@
/*
* Carla macOS utils
* Copyright (C) 2018 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

#ifdef CARLA_OS_MAC

#include "CarlaMacUtils.hpp"
#include "CarlaString.hpp"

#import <Foundation/Foundation.h>

CARLA_BACKEND_START_NAMESPACE

// --------------------------------------------------------------------------------------------------------------------

const char* findBinaryInBundle(const char* const bundleDir)
{
const CFURLRef urlRef = CFURLCreateFromFileSystemRepresentation(0, (const UInt8*)bundleDir, (CFIndex)strlen(bundleDir), true);
CARLA_SAFE_ASSERT_RETURN(urlRef != nullptr, nullptr);

const CFBundleRef bundleRef = CFBundleCreate(kCFAllocatorDefault, urlRef);
CARLA_SAFE_ASSERT_RETURN(bundleRef != nullptr, nullptr);

const CFURLRef exeRef = CFBundleCopyExecutableURL(bundleRef);
CARLA_SAFE_ASSERT_RETURN(exeRef != nullptr, nullptr);

const CFURLRef absoluteURL = CFURLCopyAbsoluteURL(exeRef);
CARLA_SAFE_ASSERT_RETURN(absoluteURL != nullptr, nullptr);

const NSString* strRef = (NSString*)CFURLCopyFileSystemPath(absoluteURL, nil);
CARLA_SAFE_ASSERT_RETURN(strRef != nullptr, nullptr);

static CarlaString ret;
ret = [strRef UTF8String];

CFRelease(absoluteURL);
CFRelease(exeRef);
CFRelease(bundleRef);
CFRelease(urlRef);

return ret.buffer();
}

// --------------------------------------------------------------------------------------------------------------------

AutoNSAutoreleasePool::AutoNSAutoreleasePool()
: pool([NSAutoreleasePool new]) {}

AutoNSAutoreleasePool::~AutoNSAutoreleasePool()
{
NSAutoreleasePool* rpool = (NSAutoreleasePool*)pool;
[rpool drain];
}

// --------------------------------------------------------------------------------------------------------------------

CARLA_BACKEND_END_NAMESPACE

#endif // CARLA_OS_MAC

+ 52
- 0
source/utils/CarlaMacUtils.hpp View File

@@ -0,0 +1,52 @@
/*
* Carla macOS utils
* Copyright (C) 2018 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

#ifndef CARLA_MAC_UTILS_HPP_INCLUDED
#define CARLA_MAC_UTILS_HPP_INCLUDED

#ifndef CARLA_OS_MAC
# error wrong include
#endif

#include "CarlaBackend.h"
CARLA_BACKEND_START_NAMESPACE

// --------------------------------------------------------------------------------------------------------------------

/*
* ...
*/
const char* findBinaryInBundle(const char* const bundleDir);

/*
* ...
*/
class AutoNSAutoreleasePool {
public:
AutoNSAutoreleasePool();
~AutoNSAutoreleasePool();

private:
void* const pool;
};

// --------------------------------------------------------------------------------------------------------------------

CARLA_BACKEND_END_NAMESPACE

#endif // CARLA_MAC_UTILS_HPP_INCLUDED

+ 6
- 2
source/utils/CarlaPatchbayUtils.hpp View File

@@ -166,7 +166,9 @@ struct ConnectionToId {

void clear() noexcept
{
id = 0;
// needed for apple GCC4.2
this->id = 0;

groupA = 0;
portA = 0;
groupB = 0;
@@ -175,7 +177,9 @@ struct ConnectionToId {

void setData(const uint i, const uint gA, const uint pA, const uint gB, const uint pB) noexcept
{
id = i;
// needed for apple GCC4.2
this->id = i;

groupA = gA;
portA = pA;
groupB = gB;


+ 26
- 4
source/utils/CarlaPipeUtils.cpp View File

@@ -937,14 +937,20 @@ bool CarlaPipeCommon::flushMessages() const noexcept
{
CARLA_SAFE_ASSERT_RETURN(pData->pipeSend != INVALID_PIPE_VALUE, false);

#if 0 // def CARLA_OS_WIN
#if defined(CARLA_OS_LINUX) || defined(CARLA_OS_GNU_HURD)
// the only call that seems to do something
return ::syncfs(pData->pipeSend) == 0;
#elif defined(CARLA_OS_WIN)
// FIXME causes issues
return true;

try {
return (::FlushFileBuffers(pData->pipeSend) != FALSE);
} CARLA_SAFE_EXCEPTION_RETURN("CarlaPipeCommon::writeMsgBuffer", false);
#endif

// nothing to do
#else
// unsupported
return true;
#endif

}

@@ -1242,6 +1248,22 @@ const char* CarlaPipeCommon::_readlineblock(const uint32_t timeOutMilliseconds)
carla_msleep(5);
}

if (std::getenv("CARLA_VALGRIND_TEST") != nullptr)
{
const uint32_t timeoutEnd2(water::Time::getMillisecondCounter() + 1000);

for (;;)
{
if (const char* const msg = _readline())
return msg;

if (water::Time::getMillisecondCounter() >= timeoutEnd2)
break;

carla_msleep(100);
}
}

carla_stderr("readlineblock timed out");
return nullptr;
}


+ 73
- 31
source/utils/CarlaPluginUI.cpp View File

@@ -23,9 +23,11 @@
# include <X11/Xatom.h>
# include <X11/Xlib.h>
# include <X11/Xutil.h>
# include "CarlaPluginUI_X11Icon.hpp"
#endif

#ifdef CARLA_OS_MAC
# include "CarlaMacUtils.hpp"
# import <Cocoa/Cocoa.h>
#endif

@@ -41,8 +43,6 @@
// X11

#ifdef HAVE_X11
# include "CarlaPluginUI_X11Icon.hpp"

typedef void (*EventProcPtr)(XEvent* ev);

static const uint X11Key_Escape = 9;
@@ -363,7 +363,9 @@ private:

#ifdef CARLA_OS_MAC

#ifdef BUILD_BRIDGE
#if defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
# define CarlaPluginWindow CARLA_JOIN_MACRO(CarlaPluginWindowBridgedArch, CARLA_PLUGIN_UI_CLASS_PREFIX)
#elif defined(BUILD_BRIDGE)
# define CarlaPluginWindow CARLA_JOIN_MACRO(CarlaPluginWindowBridged, CARLA_PLUGIN_UI_CLASS_PREFIX)
#else
# define CarlaPluginWindow CARLA_JOIN_MACRO(CarlaPluginWindow, CARLA_PLUGIN_UI_CLASS_PREFIX)
@@ -373,15 +375,16 @@ private:
{
@public
CarlaPluginUI::Callback* callback;
NSView* view;
}

- (id) initWithContentRect:(NSRect)contentRect
styleMask:(unsigned int)aStyle
backing:(NSBackingStoreType)bufferingType
defer:(BOOL)flag;
- (void) setup:(CarlaPluginUI::Callback*)cb view:(NSView*)v;
- (void) setCallback:(CarlaPluginUI::Callback*)cb;
- (BOOL) acceptsFirstResponder;
- (BOOL) canBecomeKeyWindow;
- (BOOL) canBecomeMainWindow;
- (BOOL) windowShouldClose:(id)sender;
- (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize;
@end
@@ -394,17 +397,17 @@ private:
defer:(BOOL)flag
{
callback = nil;
view = nil;

NSWindow* result = [super initWithContentRect:contentRect
styleMask:(NSClosableWindowMask |
NSTitledWindowMask |
NSResizableWindowMask)
backing:NSBackingStoreBuffered defer:NO];
backing:NSBackingStoreBuffered
defer:YES];

[result setAcceptsMouseMovedEvents:YES];
[result setContentSize:NSMakeSize(1, 1)];
[result setIsVisible:NO];
[result setAcceptsMouseMovedEvents:YES];
[result setContentSize:NSMakeSize(18, 100)];

return (CarlaPluginWindow*)result;

@@ -412,10 +415,14 @@ private:
(void)aStyle; (void)bufferingType; (void)flag;
}

- (void)setup:(CarlaPluginUI::Callback*)cb view:(NSView*)v
- (void)setCallback:(CarlaPluginUI::Callback*)cb
{
callback = cb;
view = v;
}

- (BOOL)acceptsFirstResponder
{
return YES;
}

- (BOOL)canBecomeKeyWindow
@@ -423,12 +430,17 @@ private:
return YES;
}

- (BOOL)canBecomeMainWindow
{
return NO;
}

- (BOOL)windowShouldClose:(id)sender
{
if (callback != nil)
callback->handlePluginUIClosed();

return NO;
return YES;

// unused
(void)sender;
@@ -453,20 +465,22 @@ public:
CocoaPluginUI(Callback* const cb, const uintptr_t parentId, const bool isResizable) noexcept
: CarlaPluginUI(cb, isResizable),
fView(nullptr),
fWindow(0)
fWindow(nullptr)
{
[NSAutoreleasePool new];
[NSApplication sharedApplication];
carla_debug("CocoaPluginUI::CocoaPluginUI(%p, " P_UINTPTR, "%s)", cb, parentId, bool2str(isResizable));
const CarlaBackend::AutoNSAutoreleasePool arp;

fView = [NSView new];
fView = [[NSView new]retain];
CARLA_SAFE_ASSERT_RETURN(fView != nullptr,)

[fView setHidden:YES];

if (isResizable)
[fView setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];

fWindow = [[CarlaPluginWindow new]retain];

if (fWindow == 0)
if (fWindow == nullptr)
{
[fView release];
fView = nullptr;
@@ -476,12 +490,10 @@ public:
if (! isResizable)
[[fWindow standardWindowButton:NSWindowZoomButton] setHidden:YES];

[fWindow setup:cb view:fView];
[fWindow setCallback:cb];
[fWindow setContentView:fView];
[fWindow makeFirstResponder:fView];
[fWindow makeKeyAndOrderFront:fWindow];

[NSApp activateIgnoringOtherApps:YES];
[fWindow center];

if (parentId != 0)
@@ -490,9 +502,11 @@ public:

~CocoaPluginUI() override
{
carla_debug("CocoaPluginUI::~CocoaPluginUI()");
if (fView == nullptr)
return;

[fView removeFromSuperview];
[fWindow close];
[fView release];
[fWindow release];
@@ -500,6 +514,7 @@ public:

void show() override
{
carla_debug("CocoaPluginUI::show()");
CARLA_SAFE_ASSERT_RETURN(fView != nullptr,);

[fView setHidden:NO];
@@ -508,6 +523,7 @@ public:

void hide() override
{
carla_debug("CocoaPluginUI::hide()");
CARLA_SAFE_ASSERT_RETURN(fView != nullptr,);

[fWindow setIsVisible:NO];
@@ -516,18 +532,22 @@ public:

void idle() override
{
// carla_debug("CocoaPluginUI::idle()");
}

void focus() override
{
CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
carla_debug("CocoaPluginUI::focus()");
CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);

[fWindow makeKeyWindow];
[NSApp activateIgnoringOtherApps:YES];
}

void setSize(const uint width, const uint height, const bool forceUpdate) override
{
CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
carla_debug("CocoaPluginUI::setSize(%u, %u, %s)", width, height, bool2str(forceUpdate));
CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
CARLA_SAFE_ASSERT_RETURN(fView != nullptr,);

[fView setFrame:NSMakeRect(0, 0, width, height)];
@@ -547,11 +567,18 @@ public:
[fWindow setContentMaxSize:size];
[[fWindow standardWindowButton:NSWindowZoomButton] setHidden:YES];
}

if (forceUpdate)
{
// FIXME, not enough
[fView setNeedsDisplay:YES];
}
}

void setTitle(const char* const title) override
{
CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
carla_debug("CocoaPluginUI::setTitle(\"%s\")", title);
CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);

NSString* titleString = [[NSString alloc]
initWithBytes:title
@@ -563,7 +590,8 @@ public:

void setTransientWinId(const uintptr_t winId) override
{
CARLA_SAFE_ASSERT_RETURN(fWindow != 0,);
carla_debug("CocoaPluginUI::setTransientWinId(" P_UINTPTR ")", winId);
CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);

NSWindow* const parentWindow = [NSApp windowWithWindowNumber:winId];
CARLA_SAFE_ASSERT_RETURN(parentWindow != nullptr,);
@@ -572,24 +600,27 @@ public:
ordered:NSWindowAbove];
}

void setChildWindow(void* const winId) override
void setChildWindow(void* const window) override
{
CARLA_SAFE_ASSERT_RETURN(winId != nullptr,);
carla_debug("CocoaPluginUI::setChildWindow(%p)", window);
CARLA_SAFE_ASSERT_RETURN(window != nullptr,);
}

void* getPtr() const noexcept override
{
carla_debug("CocoaPluginUI::getPtr()");
return (void*)fView;
}

void* getDisplay() const noexcept
{
carla_debug("CocoaPluginUI::getDisplay()");
return (void*)fWindow;
}

private:
NSView* fView;
id fWindow;
NSView* fView;
CarlaPluginWindow* fWindow;

CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CocoaPluginUI)
};
@@ -853,7 +884,7 @@ LRESULT CALLBACK wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)

// -----------------------------------------------------

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
bool CarlaPluginUI::tryTransientWinIdMatch(const uintptr_t pid, const char* const uiTitle, const uintptr_t winId, const bool centerUI)
{
CARLA_SAFE_ASSERT_RETURN(uiTitle != nullptr && uiTitle[0] != '\0', true);
@@ -1068,14 +1099,25 @@ bool CarlaPluginUI::tryTransientWinIdMatch(const uintptr_t pid, const char* cons

int windowToMap, windowWithPID = 0, windowWithNameAndPID = 0;

for (NSDictionary* const entry in windowList)
const NSDictionary* entry;
for (entry in windowList)
{
// FIXME: is this needed? is old version safe?
#if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
if ([entry[(id)kCGWindowSharingState] intValue] == kCGWindowSharingNone)
continue;

NSString* const windowName = entry[(id)kCGWindowName];
int const windowNumber = [entry[(id)kCGWindowNumber] intValue];
uintptr_t const windowPID = [entry[(id)kCGWindowOwnerPID] intValue];
#else
if ([[entry objectForKey:(id)kCGWindowSharingState] intValue] == kCGWindowSharingNone)
continue;

NSString* const windowName = [entry objectForKey:(id)kCGWindowName];
int const windowNumber = [[entry objectForKey:(id)kCGWindowNumber] intValue];
uintptr_t const windowPID = [[entry objectForKey:(id)kCGWindowOwnerPID] intValue];
#endif

if (windowPID != pid)
continue;
@@ -1141,7 +1183,7 @@ bool CarlaPluginUI::tryTransientWinIdMatch(const uintptr_t pid, const char* cons
return true;
(void)pid; (void)centerUI;
}
#endif // BUILD_BRIDGE
#endif // BUILD_BRIDGE_ALTERNATIVE_ARCH

// -----------------------------------------------------



+ 1
- 1
source/utils/CarlaPluginUI.hpp View File

@@ -46,7 +46,7 @@ public:
virtual void* getDisplay() const noexcept = 0;
#endif

#ifndef BUILD_BRIDGE
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
static bool tryTransientWinIdMatch(const uintptr_t pid, const char* const uiTitle, const uintptr_t winId, const bool centerUI);
#endif



Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save