| @@ -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 | |||
| @@ -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" | |||
| @@ -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 | |||
| @@ -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} | |||
| @@ -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 | |||
| } | |||
| @@ -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 | |||
| #--------------------------------------------------------------------------- | |||
| @@ -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. | |||
| */ | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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(); | |||
| } | |||
| @@ -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). | |||
| @@ -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" | |||
| @@ -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 | |||
| @@ -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); | |||
| @@ -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) | |||
| # ---------------------------------------------------------------------------------------------------------------------------- | |||
| @@ -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) | |||
| { | |||
| @@ -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; | |||
| } | |||
| @@ -1411,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" | |||
| // ----------------------------------------------------------------------- | |||
| @@ -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) | |||
| @@ -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; | |||
| @@ -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; | |||
| @@ -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(); | |||
| } | |||
| @@ -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" | |||
| @@ -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; | |||
| @@ -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 | |||
| @@ -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 \ | |||
| @@ -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 | |||
| @@ -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; | |||
| @@ -1509,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) | |||
| @@ -1531,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)); | |||
| @@ -1583,6 +1585,7 @@ public: | |||
| } // End of Post-processing | |||
| # ifndef BUILD_BRIDGE | |||
| // -------------------------------------------------------------------------------------------------------- | |||
| // Save latency values for next callback | |||
| @@ -1609,8 +1612,8 @@ public: | |||
| } | |||
| } | |||
| } | |||
| #endif // BUILD_BRIDGE | |||
| # endif | |||
| #endif // BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
| // -------------------------------------------------------------------------------------------------------- | |||
| @@ -1762,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 | |||
| @@ -1832,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]; | |||
| @@ -2224,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); | |||
| @@ -2522,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; | |||
| @@ -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; | |||
| @@ -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) | |||
| @@ -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; | |||
| @@ -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; | |||
| @@ -988,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) | |||
| @@ -1311,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; | |||
| @@ -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) | |||
| @@ -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(); | |||
| } | |||
| @@ -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 | |||
| @@ -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); | |||
| @@ -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: | |||
| @@ -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" | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| @@ -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" | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| @@ -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) | |||
| # ---------------------------------------------------------------------------------------------------------------------------- | |||
| @@ -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" | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| @@ -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); | |||
| } | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| @@ -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 | |||
| } | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| @@ -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; | |||
| @@ -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 \ | |||
| @@ -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 | |||
| // --------------------------------------------------------------------- | |||
| @@ -226,5 +226,6 @@ CARLA_BRIDGE_UI_END_NAMESPACE | |||
| #define CARLA_PLUGIN_UI_CLASS_PREFIX ToolkitNative | |||
| #include "CarlaPluginUI.cpp" | |||
| #include "CarlaMacUtils.cpp" | |||
| // ------------------------------------------------------------------------- | |||
| @@ -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() | |||
| @@ -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 | |||
| @@ -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)) | |||
| @@ -2884,7 +2904,10 @@ class CarlaHostPlugin(CarlaHostMeta): | |||
| return self.sendMsgAndSetError(["replace_plugin", pluginId]) | |||
| def switch_plugins(self, pluginIdA, pluginIdB): | |||
| return self.sendMsgAndSetError(["switch_plugins", pluginIdA, pluginIdB]) | |||
| ret = self.sendMsgAndSetError(["switch_plugins", pluginIdA, pluginIdB]) | |||
| if ret: | |||
| self._switchPlugins(pluginIdA, pluginIdB) | |||
| return ret | |||
| def load_plugin_state(self, pluginId, filename): | |||
| return self.sendMsgAndSetError(["load_plugin_state", pluginId, filename]) | |||
| @@ -2926,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 "" | |||
| @@ -3242,4 +3271,9 @@ class CarlaHostPlugin(CarlaHostMeta): | |||
| def _set_peaks(self, pluginId, in1, in2, out1, out2): | |||
| self.fPluginsInfo[pluginId].peaks = [in1, in2, out1, out2] | |||
| def _switchPlugins(self, pluginIdA, pluginIdB): | |||
| tmp = self.fPluginsInfo[pluginIdA] | |||
| self.fPluginsInfo[pluginIdA] = self.fPluginsInfo[pluginIdB] | |||
| self.fPluginsInfo[pluginIdB] = tmp | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| @@ -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 | |||
| @@ -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() | |||
| # -------------------------------------------------------------------------------------------------------- | |||
| @@ -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 | |||
| @@ -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): | |||
| @@ -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 | |||
| @@ -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"))) | |||
| @@ -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) | |||
| @@ -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; | |||
| @@ -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 | |||
| @@ -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; | |||
| @@ -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); | |||
| @@ -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; | |||
| @@ -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 | |||
| @@ -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]; | |||
| @@ -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"; | |||
| @@ -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 | |||
| } | |||
| @@ -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; | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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" | |||
| @@ -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 | |||
| @@ -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 | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| @@ -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 | |||
| }; | |||
| @@ -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; | |||
| @@ -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): | |||
| @@ -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)" | |||
| @@ -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); | |||
| } | |||
| @@ -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 | |||
| @@ -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" | |||
| // ----------------------------------------------------------------------- | |||
| @@ -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 | |||
| // ----------------------------------------------------------------------- | |||
| @@ -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) | |||
| # ---------------------------------------------------------------------------------------------------------------------- | |||
| @@ -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; | |||
| } | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| @@ -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 | |||
| @@ -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) } } ); | |||
| } | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| @@ -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: | |||
| @@ -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 | |||
| @@ -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; | |||
| @@ -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; | |||
| @@ -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; | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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; | |||
| @@ -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; | |||
| } | |||
| @@ -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 | |||
| // ----------------------------------------------------- | |||
| @@ -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 | |||
| @@ -122,7 +122,7 @@ CarlaStateSave::Parameter::Parameter() noexcept | |||
| index(-1), | |||
| name(nullptr), | |||
| symbol(nullptr), | |||
| #ifndef BUILD_BRIDGE | |||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
| value(0.0f), | |||
| midiChannel(0), | |||
| midiCC(-1) {} | |||
| @@ -189,7 +189,7 @@ CarlaStateSave::CarlaStateSave() noexcept | |||
| binary(nullptr), | |||
| uniqueId(0), | |||
| options(0x0), | |||
| #ifndef BUILD_BRIDGE | |||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
| active(false), | |||
| dryWet(1.0f), | |||
| volume(1.0f), | |||
| @@ -247,7 +247,7 @@ void CarlaStateSave::clear() noexcept | |||
| uniqueId = 0; | |||
| options = 0x0; | |||
| #ifndef BUILD_BRIDGE | |||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
| active = false; | |||
| dryWet = 1.0f; | |||
| volume = 1.0f; | |||
| @@ -323,7 +323,7 @@ bool CarlaStateSave::fillFromXmlElement(const XmlElement* const xmlElement) | |||
| const String& tag(xmlData->getTagName()); | |||
| const String text(xmlData->getAllSubText().trim()); | |||
| #ifndef BUILD_BRIDGE | |||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
| // ------------------------------------------------------- | |||
| // Internal Data | |||
| @@ -431,7 +431,7 @@ bool CarlaStateSave::fillFromXmlElement(const XmlElement* const xmlElement) | |||
| stateParameter->dummy = false; | |||
| stateParameter->value = pText.getFloatValue(); | |||
| } | |||
| #ifndef BUILD_BRIDGE | |||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
| else if (pTag == "MidiChannel") | |||
| { | |||
| const int channel(pText.getIntValue()); | |||
| @@ -545,7 +545,7 @@ void CarlaStateSave::dumpToMemoryStream(MemoryOutputStream& content) const | |||
| content << " <Data>\n"; | |||
| #ifndef BUILD_BRIDGE | |||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
| { | |||
| MemoryOutputStream dataXml; | |||
| @@ -588,7 +588,7 @@ void CarlaStateSave::dumpToMemoryStream(MemoryOutputStream& content) const | |||
| if (stateParameter->symbol != nullptr && stateParameter->symbol[0] != '\0') | |||
| parameterXml << " <Symbol>" << xmlSafeString(stateParameter->symbol, true) << "</Symbol>\n"; | |||
| #ifndef BUILD_BRIDGE | |||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
| if (stateParameter->midiCC > 0) | |||
| { | |||
| parameterXml << " <MidiCC>" << stateParameter->midiCC << "</MidiCC>\n"; | |||
| @@ -34,7 +34,7 @@ struct CarlaStateSave { | |||
| const char* name; | |||
| const char* symbol; | |||
| float value; | |||
| #ifndef BUILD_BRIDGE | |||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
| uint8_t midiChannel; | |||
| int16_t midiCC; | |||
| #endif | |||
| @@ -70,7 +70,7 @@ struct CarlaStateSave { | |||
| int64_t uniqueId; | |||
| uint options; | |||
| #ifndef BUILD_BRIDGE | |||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
| bool active; | |||
| float dryWet; | |||
| float volume; | |||
| @@ -159,6 +159,7 @@ public: | |||
| return *this; | |||
| } | |||
| #if 0 | |||
| T& operator*() noexcept | |||
| { | |||
| static T& fallback(_getFallback()); | |||
| @@ -168,6 +169,7 @@ public: | |||
| return data->value; | |||
| } | |||
| #endif | |||
| const T& operator*() const noexcept | |||
| { | |||
| @@ -180,8 +182,8 @@ public: | |||
| } | |||
| private: | |||
| ListHead* fEntry; | |||
| ListHead* fEntry2; | |||
| const ListHead* fEntry; | |||
| const ListHead* fEntry2; | |||
| static T& _getFallback() | |||
| { | |||
| @@ -63,16 +63,10 @@ class RackListItem(QListWidgetItem): | |||
| self.fWidget = None | |||
| self.fOptions = { | |||
| 'compact': False, | |||
| 'compact': bool(self.host.get_custom_data_value(pluginId, CUSTOM_DATA_TYPE_PROPERTY, "CarlaSkinIsCompacted") == "true"), | |||
| 'useSkins': useSkins | |||
| } | |||
| 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": | |||
| self.fOptions['compact'] = bool(cdata['value'] == "true") | |||
| break | |||
| self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled) | |||
| #self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled|Qt.ItemIsDragEnabled) | |||