@@ -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; | |||
} | |||
@@ -1190,6 +1190,8 @@ protected: | |||
} | |||
case kPluginBridgeRtClientProcess: { | |||
const uint32_t frames(fShmRtClientControl.readUInt()); | |||
CARLA_SAFE_ASSERT_BREAK(fShmAudioPool.data != nullptr); | |||
if (plugin != nullptr && plugin->isEnabled() && plugin->tryLock(fIsOffline)) | |||
@@ -1240,7 +1242,7 @@ protected: | |||
} | |||
plugin->initBuffers(); | |||
plugin->process(audioIn, audioOut, cvIn, cvOut, pData->bufferSize); | |||
plugin->process(audioIn, audioOut, cvIn, cvOut, frames); | |||
plugin->unlock(); | |||
} | |||
@@ -1409,14 +1411,6 @@ CARLA_BACKEND_END_NAMESPACE | |||
// ----------------------------------------------------------------------- | |||
#if defined(CARLA_OS_WIN) && ! defined(__WINE__) | |||
extern "C" __declspec (dllexport) | |||
#else | |||
extern "C" __attribute__ ((visibility("default"))) | |||
#endif | |||
void carla_register_native_plugin_carla(); | |||
void carla_register_native_plugin_carla(){} | |||
#include "CarlaBridgeUtils.cpp" | |||
// ----------------------------------------------------------------------- |
@@ -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; | |||
@@ -1494,6 +1494,7 @@ public: | |||
{ | |||
fShmRtClientControl.writeOpcode(kPluginBridgeRtClientProcess); | |||
fShmRtClientControl.writeUInt(frames); | |||
fShmRtClientControl.commitWrite(); | |||
} | |||
@@ -1508,7 +1509,7 @@ public: | |||
for (uint32_t i=0; i < fInfo.aOuts; ++i) | |||
carla_copyFloats(audioOut[i], fShmAudioPool.data + ((i + fInfo.aIns) * frames), frames); | |||
#ifndef BUILD_BRIDGE | |||
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
// -------------------------------------------------------------------------------------------------------- | |||
// Post-processing (dry/wet, volume and balance) | |||
@@ -1530,11 +1531,13 @@ public: | |||
for (uint32_t k=0; k < frames; ++k) | |||
{ | |||
# ifndef BUILD_BRIDGE | |||
if (k < pData->latency.frames) | |||
bufValue = pData->latency.buffers[c][k]; | |||
else if (pData->latency.frames < frames) | |||
bufValue = audioIn[c][k-pData->latency.frames]; | |||
else | |||
# endif | |||
bufValue = audioIn[c][k]; | |||
audioOut[i][k] = (audioOut[i][k] * pData->postProc.dryWet) + (bufValue * (1.0f - pData->postProc.dryWet)); | |||
@@ -1582,6 +1585,7 @@ public: | |||
} // End of Post-processing | |||
# ifndef BUILD_BRIDGE | |||
// -------------------------------------------------------------------------------------------------------- | |||
// Save latency values for next callback | |||
@@ -1608,8 +1612,8 @@ public: | |||
} | |||
} | |||
} | |||
#endif // BUILD_BRIDGE | |||
# endif | |||
#endif // BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
// -------------------------------------------------------------------------------------------------------- | |||
@@ -1761,7 +1765,7 @@ public: | |||
{ | |||
const PluginBridgeNonRtServerOpcode opcode(fShmNonRtServerControl.readOpcode()); | |||
#ifdef DEBUG | |||
if (opcode != kPluginBridgeNonRtServerPong) { | |||
if (opcode != kPluginBridgeNonRtServerPong && opcode != kPluginBridgeNonRtServerParameterValue2) { | |||
carla_debug("CarlaPluginBridge::handleNonRtData() - got opcode: %s", PluginBridgeNonRtServerOpcode2str(opcode)); | |||
} | |||
#endif | |||
@@ -1831,21 +1835,11 @@ public: | |||
case kPluginBridgeNonRtServerAudioCount: { | |||
// uint/ins, uint/outs | |||
fInfo.clear(); | |||
fInfo.aIns = fShmNonRtServerControl.readUInt(); | |||
fInfo.aOuts = fShmNonRtServerControl.readUInt(); | |||
if (fInfo.aInNames != nullptr) | |||
{ | |||
delete[] fInfo.aInNames; | |||
fInfo.aInNames = nullptr; | |||
} | |||
if (fInfo.aOutNames != nullptr) | |||
{ | |||
delete[] fInfo.aOutNames; | |||
fInfo.aOutNames = nullptr; | |||
} | |||
if (fInfo.aIns > 0) | |||
{ | |||
fInfo.aInNames = new const char*[fInfo.aIns]; | |||
@@ -2223,7 +2217,7 @@ public: | |||
break; | |||
case kPluginBridgeNonRtServerUiClosed: | |||
#ifndef BUILD_BRIDGE | |||
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
pData->transientTryCounter = 0; | |||
#endif | |||
pData->engine->callback(ENGINE_CALLBACK_UI_STATE_CHANGED, pData->id, 0, 0, 0.0f, nullptr); | |||
@@ -2521,6 +2515,38 @@ private: | |||
aOutNames(nullptr), | |||
chunk() {} | |||
~Info() | |||
{ | |||
clear(); | |||
} | |||
void clear() | |||
{ | |||
if (aInNames != nullptr) | |||
{ | |||
CARLA_SAFE_ASSERT_INT(aIns > 0, aIns); | |||
for (uint32_t i=0; i<aIns; ++i) | |||
delete[] aInNames[i]; | |||
delete[] aInNames; | |||
aInNames = nullptr; | |||
} | |||
if (aOutNames != nullptr) | |||
{ | |||
CARLA_SAFE_ASSERT_INT(aOuts > 0, aOuts); | |||
for (uint32_t i=0; i<aOuts; ++i) | |||
delete[] aOutNames[i]; | |||
delete[] aOutNames; | |||
aOutNames = nullptr; | |||
} | |||
aIns = aOuts = 0; | |||
} | |||
CARLA_DECLARE_NON_COPY_STRUCT(Info) | |||
} fInfo; | |||
@@ -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; | |||
@@ -967,6 +967,7 @@ public: | |||
{ | |||
fShmRtClientControl.writeOpcode(kPluginBridgeRtClientProcess); | |||
fShmRtClientControl.writeUInt(frames); | |||
fShmRtClientControl.commitWrite(); | |||
} | |||
@@ -987,7 +988,7 @@ public: | |||
for (uint32_t i=0; i < fInfo.aOuts; ++i) | |||
carla_copyFloats(audioOut[i], fShmAudioPool.data + ((i + fInfo.aIns) * frames), frames); | |||
#ifndef BUILD_BRIDGE | |||
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
// -------------------------------------------------------------------------------------------------------- | |||
// Post-processing (dry/wet, volume and balance) | |||
@@ -1310,7 +1311,7 @@ public: | |||
// FIXME dryWet broken | |||
pData->hints = PLUGIN_IS_BRIDGE | PLUGIN_OPTION_FIXED_BUFFERS; | |||
#ifndef BUILD_BRIDGE | |||
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
pData->hints |= /*PLUGIN_CAN_DRYWET |*/ PLUGIN_CAN_VOLUME | PLUGIN_CAN_BALANCE; | |||
#endif | |||
//fInfo.optionsAvailable = optionAv; | |||
@@ -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)) | |||
@@ -2929,6 +2949,12 @@ class CarlaHostPlugin(CarlaHostMeta): | |||
def get_custom_data(self, pluginId, customDataId): | |||
return self.fPluginsInfo[pluginId].customData[customDataId] | |||
def get_custom_data_value(self, pluginId, type_, key): | |||
for customData in self.fPluginsInfo[pluginId].customData: | |||
if customData['type'] == type_ and customData['key'] == key: | |||
return customData['value'] | |||
return "" | |||
def get_chunk_data(self, pluginId): | |||
return "" | |||
@@ -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 | |||
@@ -527,6 +527,12 @@ class AbstractPluginSlot(QFrame, PluginEditParentMeta): | |||
QLabel#label_name:disabled { color: #555; } | |||
""" % styleSheet2 | |||
styleSheet += """ | |||
QComboBox#cb_presets, | |||
QLabel#label_audio_in, | |||
QLabel#label_audio_out, | |||
QLabel#label_midi { font-size: 10px; } | |||
""" | |||
self.setStyleSheet(styleSheet) | |||
# ------------------------------------------------------------- | |||
@@ -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 | |||
@@ -571,6 +571,11 @@ bool CarlaJackAppClient::handleRtData() | |||
} | |||
case kPluginBridgeRtClientProcess: { | |||
const uint32_t frames(fShmRtClientControl.readUInt()); | |||
CARLA_SAFE_ASSERT_UINT2_BREAK(frames == fServer.bufferSize, frames, fServer.bufferSize); | |||
// TODO tell client of xrun in case buffersize does not match | |||
// FIXME - lock if offline | |||
const CarlaMutexTryLocker cmtl(fRealtimeThreadMutex); | |||
@@ -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): | |||
@@ -760,15 +760,7 @@ def getGroupPos(group_id, port_mode=PORT_MODE_OUTPUT): | |||
for group in canvas.group_list: | |||
if group.group_id == group_id: | |||
if group.split: | |||
if port_mode == PORT_MODE_OUTPUT: | |||
return group.widgets[0].pos() | |||
elif port_mode == PORT_MODE_INPUT: | |||
return group.widgets[1].pos() | |||
else: | |||
return QPointF(0, 0) | |||
else: | |||
return group.widgets[0].pos() | |||
return group.widgets[1 if (group.split and port_mode == PORT_MODE_INPUT) else 0].pos() | |||
qCritical("PatchCanvas::getGroupPos(%i, %s) - unable to find group" % (group_id, port_mode2str(port_mode))) | |||
return QPointF(0, 0) | |||
@@ -1266,26 +1258,16 @@ class PatchScene(QGraphicsScene): | |||
rect = item.boundingRect() | |||
if first_value: | |||
first_value = False | |||
min_x = pos.x() | |||
elif pos.x() < min_x: | |||
min_x = pos.x() | |||
if first_value: | |||
min_y = pos.y() | |||
elif pos.y() < min_y: | |||
min_y = pos.y() | |||
if first_value: | |||
max_x = pos.x() + rect.width() | |||
elif pos.x() + rect.width() > max_x: | |||
max_x = pos.x() + rect.width() | |||
if first_value: | |||
max_y = pos.y() + rect.height() | |||
elif pos.y() + rect.height() > max_y: | |||
max_y = pos.y() + rect.height() | |||
first_value = False | |||
else: | |||
min_x = min(min_x, pos.x()) | |||
min_y = min(min_y, pos.y()) | |||
max_x = max(max_x, pos.x() + rect.width()) | |||
max_y = max(max_y, pos.y() + rect.height()) | |||
if not first_value: | |||
self.m_view.fitInView(min_x, min_y, abs(max_x - min_x), abs(max_y - min_y), Qt.KeepAspectRatio) | |||
@@ -1390,19 +1372,14 @@ class PatchScene(QGraphicsScene): | |||
self.m_rubberband_orig_point = event.scenePos() | |||
pos = event.scenePos() | |||
if pos.x() > self.m_rubberband_orig_point.x(): | |||
x = self.m_rubberband_orig_point.x() | |||
else: | |||
x = pos.x() | |||
if pos.y() > self.m_rubberband_orig_point.y(): | |||
y = self.m_rubberband_orig_point.y() | |||
else: | |||
y = pos.y() | |||
x = min(pos.x(), self.m_rubberband_orig_point.x()) | |||
y = min(pos.y(), self.m_rubberband_orig_point.y()) | |||
lineHinting = canvas.theme.rubberband_pen.widthF() / 2 | |||
self.m_rubberband.setRect(x+lineHinting, y+lineHinting, abs(pos.x() - self.m_rubberband_orig_point.x()), abs(pos.y() - self.m_rubberband_orig_point.y())) | |||
self.m_rubberband.setRect(x+lineHinting, | |||
y+lineHinting, | |||
abs(pos.x() - self.m_rubberband_orig_point.x()), | |||
abs(pos.y() - self.m_rubberband_orig_point.y())) | |||
return event.accept() | |||
if self.m_mid_button_down and self.m_ctrl_down: | |||
@@ -1570,10 +1547,12 @@ class CanvasLine(QGraphicsLineItem): | |||
def updateLinePos(self): | |||
if self.item1.getPortMode() == PORT_MODE_OUTPUT: | |||
line = QLineF(self.item1.scenePos().x() + self.item1.getPortWidth() + 12, | |||
self.item1.scenePos().y() + float(canvas.theme.port_height)/2, | |||
self.item2.scenePos().x(), | |||
self.item2.scenePos().y() + float(canvas.theme.port_height)/2) | |||
rect1 = self.item1.sceneBoundingRect() | |||
rect2 = self.item2.sceneBoundingRect() | |||
line = QLineF(rect1.right(), | |||
rect1.top() + float(canvas.theme.port_height)/2, | |||
rect2.left(), | |||
rect2.top() + float(canvas.theme.port_height)/2) | |||
self.setLine(line) | |||
self.m_lineSelected = False | |||
@@ -1669,17 +1648,15 @@ class CanvasBezierLine(QGraphicsPathItem): | |||
def updateLinePos(self): | |||
if self.item1.getPortMode() == PORT_MODE_OUTPUT: | |||
item1_x = self.item1.scenePos().x() + self.item1.getPortWidth() + 12 | |||
item1_y = self.item1.scenePos().y() + float(canvas.theme.port_height)/2 | |||
item2_x = self.item2.scenePos().x() | |||
item2_y = self.item2.scenePos().y() + float(canvas.theme.port_height)/2 | |||
rect1 = self.item1.sceneBoundingRect() | |||
rect2 = self.item2.sceneBoundingRect() | |||
item1_mid_x = abs(item1_x - item2_x) / 2 | |||
item1_new_x = item1_x + item1_mid_x | |||
item2_mid_x = abs(item1_x - item2_x) / 2 | |||
item2_new_x = item2_x - item2_mid_x | |||
item1_x = rect1.right() | |||
item2_x = rect2.left() | |||
item1_y = rect1.top() + float(canvas.theme.port_height)/2 | |||
item2_y = rect2.top() + float(canvas.theme.port_height)/2 | |||
item1_new_x = item1_x + abs(item1_x - item2_x) / 2 | |||
item2_new_x = item2_x - abs(item1_x - item2_x) / 2 | |||
path = QPainterPath(QPointF(item1_x, item1_y)) | |||
path.cubicTo(item1_new_x, item1_y, item2_new_x, item2_y, item2_x, item2_y) | |||
@@ -2092,11 +2069,14 @@ class CanvasPort(QGraphicsItem): | |||
painter.save() | |||
painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing == ANTIALIASING_FULL)) | |||
# FIXME: would be more correct is to take line width from Pen, loaded to painter, | |||
# FIXME: would be more correct to take line width from Pen, loaded to painter, | |||
# but this needs some code rearrangement | |||
lineHinting = canvas.theme.port_audio_jack_pen.widthF() / 2 | |||
poly_locx = [0, 0, 0, 0, 0] | |||
poly_corner_xhinting = (float(canvas.theme.port_height)/2) % floor(float(canvas.theme.port_height)/2) | |||
if poly_corner_xhinting == 0: | |||
poly_corner_xhinting = 0.5 * (1 - 7 / (float(canvas.theme.port_height)/2)) | |||
if self.m_port_mode == PORT_MODE_INPUT: | |||
text_pos = QPointF(3, canvas.theme.port_text_ypos) | |||
@@ -2104,7 +2084,7 @@ class CanvasPort(QGraphicsItem): | |||
if canvas.theme.port_mode == Theme.THEME_PORT_POLYGON: | |||
poly_locx[0] = lineHinting | |||
poly_locx[1] = self.m_port_width + 5 - lineHinting | |||
poly_locx[2] = self.m_port_width + 12 - lineHinting | |||
poly_locx[2] = self.m_port_width + 12 - poly_corner_xhinting | |||
poly_locx[3] = self.m_port_width + 5 - lineHinting | |||
poly_locx[4] = lineHinting | |||
elif canvas.theme.port_mode == Theme.THEME_PORT_SQUARE: | |||
@@ -2123,7 +2103,7 @@ class CanvasPort(QGraphicsItem): | |||
if canvas.theme.port_mode == Theme.THEME_PORT_POLYGON: | |||
poly_locx[0] = self.m_port_width + 12 - lineHinting | |||
poly_locx[1] = 7 + lineHinting | |||
poly_locx[2] = 0 + lineHinting | |||
poly_locx[2] = 0 + poly_corner_xhinting | |||
poly_locx[3] = 7 + lineHinting | |||
poly_locx[4] = self.m_port_width + 12 - lineHinting | |||
elif canvas.theme.port_mode == Theme.THEME_PORT_SQUARE: | |||
@@ -2407,23 +2387,9 @@ class CanvasBox(QGraphicsItem): | |||
def updatePositions(self): | |||
self.prepareGeometryChange() | |||
max_in_width = 0 | |||
max_in_height = canvas.theme.box_header_height + canvas.theme.box_header_spacing | |||
max_out_width = 0 | |||
max_out_height = canvas.theme.box_header_height + canvas.theme.box_header_spacing | |||
port_spacing = canvas.theme.port_height + canvas.theme.port_spacing | |||
have_audio_jack_in = have_midi_jack_in = have_midi_alsa_in = have_parameter_in = False | |||
have_audio_jack_out = have_midi_jack_out = have_midi_alsa_out = have_parameter_out = False | |||
# reset box size | |||
self.p_width = 50 | |||
self.p_height = canvas.theme.box_header_height + canvas.theme.box_header_spacing + 1 | |||
# Check Text Name size | |||
app_name_size = QFontMetrics(self.m_font_name).width(self.m_group_name) + 30 | |||
if app_name_size > self.p_width: | |||
self.p_width = app_name_size | |||
self.p_width = max( 50, app_name_size ) | |||
# Get Port List | |||
port_list = [] | |||
@@ -2431,175 +2397,60 @@ class CanvasBox(QGraphicsItem): | |||
if port.group_id == self.m_group_id and port.port_id in self.m_port_list_ids: | |||
port_list.append(port) | |||
# Get Max Box Width/Height | |||
for port in port_list: | |||
if port.port_mode == PORT_MODE_INPUT: | |||
max_in_height += port_spacing | |||
size = QFontMetrics(self.m_font_port).width(port.port_name) | |||
if size > max_in_width: | |||
max_in_width = size | |||
if port.port_type == PORT_TYPE_AUDIO_JACK and not have_audio_jack_in: | |||
have_audio_jack_in = True | |||
max_in_height += canvas.theme.port_spacingT | |||
elif port.port_type == PORT_TYPE_MIDI_JACK and not have_midi_jack_in: | |||
have_midi_jack_in = True | |||
max_in_height += canvas.theme.port_spacingT | |||
elif port.port_type == PORT_TYPE_MIDI_ALSA and not have_midi_alsa_in: | |||
have_midi_alsa_in = True | |||
max_in_height += canvas.theme.port_spacingT | |||
elif port.port_type == PORT_TYPE_PARAMETER and not have_parameter_in: | |||
have_parameter_in = True | |||
max_in_height += canvas.theme.port_spacingT | |||
elif port.port_mode == PORT_MODE_OUTPUT: | |||
max_out_height += port_spacing | |||
size = QFontMetrics(self.m_font_port).width(port.port_name) | |||
if size > max_out_width: | |||
max_out_width = size | |||
if port.port_type == PORT_TYPE_AUDIO_JACK and not have_audio_jack_out: | |||
have_audio_jack_out = True | |||
max_out_height += canvas.theme.port_spacingT | |||
elif port.port_type == PORT_TYPE_MIDI_JACK and not have_midi_jack_out: | |||
have_midi_jack_out = True | |||
max_out_height += canvas.theme.port_spacingT | |||
elif port.port_type == PORT_TYPE_MIDI_ALSA and not have_midi_alsa_out: | |||
have_midi_alsa_out = True | |||
max_out_height += canvas.theme.port_spacingT | |||
elif port.port_type == PORT_TYPE_PARAMETER and not have_parameter_out: | |||
have_parameter_out = True | |||
max_out_height += canvas.theme.port_spacingT | |||
if canvas.theme.port_spacingT == 0: | |||
max_in_height += 2 | |||
max_out_height += 2 | |||
final_width = 30 + max_in_width + max_out_width | |||
if final_width > self.p_width: | |||
self.p_width = final_width | |||
if max_in_height > self.p_height: | |||
self.p_height = max_in_height | |||
if max_out_height > self.p_height: | |||
self.p_height = max_out_height | |||
# Remove bottom space | |||
self.p_height -= canvas.theme.port_spacingT | |||
if canvas.theme.box_header_spacing > 0: | |||
if len(port_list) == 0: | |||
self.p_height -= canvas.theme.box_header_spacing | |||
else: | |||
self.p_height -= canvas.theme.box_header_spacing/2 | |||
last_in_pos = canvas.theme.box_header_height + canvas.theme.box_header_spacing | |||
last_out_pos = canvas.theme.box_header_height + canvas.theme.box_header_spacing | |||
last_in_type = PORT_TYPE_NULL | |||
last_out_type = PORT_TYPE_NULL | |||
# Re-position ports, AUDIO_JACK | |||
for port in port_list: | |||
if port.port_type != PORT_TYPE_AUDIO_JACK: | |||
continue | |||
if port.port_mode == PORT_MODE_INPUT: | |||
if last_in_type != PORT_TYPE_NULL and port.port_type != last_in_type: | |||
last_in_pos += canvas.theme.port_spacingT | |||
port.widget.setPos(QPointF(1 + canvas.theme.port_offset, last_in_pos)) | |||
port.widget.setPortWidth(max_in_width) | |||
last_in_pos += port_spacing | |||
last_in_type = port.port_type | |||
elif port.port_mode == PORT_MODE_OUTPUT: | |||
if last_out_type != PORT_TYPE_NULL and port.port_type != last_out_type: | |||
last_out_pos += canvas.theme.port_spacingT | |||
port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 13, last_out_pos)) | |||
port.widget.setPortWidth(max_out_width) | |||
last_out_pos += port_spacing | |||
last_out_type = port.port_type | |||
# Re-position ports, MIDI_JACK | |||
for port in port_list: | |||
if port.port_type != PORT_TYPE_MIDI_JACK: | |||
continue | |||
if port.port_mode == PORT_MODE_INPUT: | |||
if last_in_type != PORT_TYPE_NULL and port.port_type != last_in_type: | |||
last_in_pos += canvas.theme.port_spacingT | |||
port.widget.setPos(QPointF(1 + canvas.theme.port_offset, last_in_pos)) | |||
port.widget.setPortWidth(max_in_width) | |||
last_in_pos += port_spacing | |||
last_in_type = port.port_type | |||
elif port.port_mode == PORT_MODE_OUTPUT: | |||
if last_out_type != PORT_TYPE_NULL and port.port_type != last_out_type: | |||
last_out_pos += canvas.theme.port_spacingT | |||
port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 13, last_out_pos)) | |||
port.widget.setPortWidth(max_out_width) | |||
last_out_pos += port_spacing | |||
last_out_type = port.port_type | |||
# Re-position ports, MIDI_ALSA | |||
for port in port_list: | |||
if port.port_type != PORT_TYPE_MIDI_ALSA: | |||
continue | |||
if port.port_mode == PORT_MODE_INPUT: | |||
if last_in_type != PORT_TYPE_NULL and port.port_type != last_in_type: | |||
last_in_pos += canvas.theme.port_spacingT | |||
port.widget.setPos(QPointF(1 + canvas.theme.port_offset, last_in_pos)) | |||
port.widget.setPortWidth(max_in_width) | |||
last_in_pos += port_spacing | |||
last_in_type = port.port_type | |||
elif port.port_mode == PORT_MODE_OUTPUT: | |||
if last_out_type != PORT_TYPE_NULL and port.port_type != last_out_type: | |||
last_out_pos += canvas.theme.port_spacingT | |||
port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 13, last_out_pos)) | |||
port.widget.setPortWidth(max_out_width) | |||
last_out_pos += port_spacing | |||
last_out_type = port.port_type | |||
# Re-position ports, PARAMETER | |||
for port in port_list: | |||
if port.port_type != PORT_TYPE_PARAMETER: | |||
continue | |||
if port.port_mode == PORT_MODE_INPUT: | |||
if last_in_type != PORT_TYPE_NULL and port.port_type != last_in_type: | |||
last_in_pos += canvas.theme.port_spacingT | |||
port.widget.setPos(QPointF(1 + canvas.theme.port_offset, last_in_pos)) | |||
port.widget.setPortWidth(max_in_width) | |||
last_in_pos += port_spacing | |||
last_in_type = port.port_type | |||
elif port.port_mode == PORT_MODE_OUTPUT: | |||
if last_out_type != PORT_TYPE_NULL and port.port_type != last_out_type: | |||
last_out_pos += canvas.theme.port_spacingT | |||
port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 13, last_out_pos)) | |||
port.widget.setPortWidth(max_out_width) | |||
last_out_pos += port_spacing | |||
last_out_type = port.port_type | |||
if len(port_list) == 0: | |||
self.p_height = canvas.theme.box_header_height | |||
else: | |||
max_in_width = max_out_width = 0 | |||
port_spacing = canvas.theme.port_height + canvas.theme.port_spacing | |||
# Get Max Box Width, vertical ports re-positioning | |||
port_types = [PORT_TYPE_AUDIO_JACK, PORT_TYPE_MIDI_JACK, PORT_TYPE_MIDI_ALSA, PORT_TYPE_PARAMETER] | |||
last_in_type = last_out_type = PORT_TYPE_NULL | |||
last_in_pos = last_out_pos = canvas.theme.box_header_height + canvas.theme.box_header_spacing | |||
for port_type in port_types: | |||
for port in port_list: | |||
if port.port_type != port_type: | |||
continue | |||
size = QFontMetrics(self.m_font_port).width(port.port_name) | |||
if port.port_mode == PORT_MODE_INPUT: | |||
max_in_width = max( max_in_width, size ) | |||
if port.port_type != last_in_type: | |||
if last_in_type != PORT_TYPE_NULL: | |||
last_in_pos += canvas.theme.port_spacingT | |||
last_in_type = port.port_type | |||
port.widget.setY(last_in_pos) | |||
last_in_pos += port_spacing | |||
elif port.port_mode == PORT_MODE_OUTPUT: | |||
max_out_width = max( max_out_width, size ) | |||
if port.port_type != last_out_type: | |||
if last_out_type != PORT_TYPE_NULL: | |||
last_out_pos += canvas.theme.port_spacingT | |||
last_out_type = port.port_type | |||
port.widget.setY(last_out_pos) | |||
last_out_pos += port_spacing | |||
self.p_width = max(self.p_width, 30 + max_in_width + max_out_width) | |||
# Horizontal ports re-positioning | |||
inX = 1 + canvas.theme.port_offset | |||
outX = self.p_width - max_out_width - canvas.theme.port_offset - 13 | |||
for port_type in port_types: | |||
for port in port_list: | |||
if port.port_mode == PORT_MODE_INPUT: | |||
port.widget.setX(inX) | |||
port.widget.setPortWidth(max_in_width) | |||
elif port.port_mode == PORT_MODE_OUTPUT: | |||
port.widget.setX(outX) | |||
port.widget.setPortWidth(max_out_width) | |||
self.p_height = max(last_in_pos, last_out_pos) | |||
self.p_height += max(canvas.theme.port_spacing, canvas.theme.port_spacingT) - canvas.theme.port_spacing | |||
self.p_height += canvas.theme.box_pen.widthF() | |||
self.repaintLines(True) | |||
self.update() | |||
@@ -2801,12 +2652,12 @@ class CanvasBox(QGraphicsItem): | |||
def paint(self, painter, option, widget): | |||
painter.save() | |||
painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing == ANTIALIASING_FULL)) | |||
rect = QRectF(0, 0, self.p_width, self.p_height) | |||
# Draw rectangle | |||
if self.isSelected(): | |||
painter.setPen(canvas.theme.box_pen_sel) | |||
else: | |||
painter.setPen(canvas.theme.box_pen) | |||
pen = canvas.theme.box_pen_sel if self.isSelected() else canvas.theme.box_pen | |||
painter.setPen(pen) | |||
lineHinting = pen.widthF() / 2 | |||
if canvas.theme.box_bg_type == Theme.THEME_BG_GRADIENT: | |||
box_gradient = QLinearGradient(0, 0, 0, self.p_height) | |||
@@ -2816,18 +2667,21 @@ class CanvasBox(QGraphicsItem): | |||
else: | |||
painter.setBrush(canvas.theme.box_bg_1) | |||
lineHinting = painter.pen().widthF() / 2 | |||
painter.drawRect(QRectF(lineHinting, lineHinting, self.p_width - lineHinting*2, self.p_height - lineHinting*2)) | |||
rect.adjust(lineHinting, lineHinting, -lineHinting, -lineHinting) | |||
painter.drawRect(rect) | |||
# Draw pixmap header | |||
rect.setHeight(canvas.theme.box_header_height) | |||
if canvas.theme.box_header_pixmap: | |||
painter.setPen(Qt.NoPen) | |||
painter.setBrush(canvas.theme.box_bg_2) | |||
painter.drawRect(1, 1, self.p_width-2, canvas.theme.box_header_height) | |||
headerPos = QPointF(1, 1) | |||
headerRect = QRectF(2, 2, self.p_width-4, canvas.theme.box_header_height-3) | |||
painter.drawTiledPixmap(headerRect, canvas.theme.box_header_pixmap, headerPos) | |||
# outline | |||
rect.adjust(lineHinting, lineHinting, -lineHinting, -lineHinting) | |||
painter.drawRect(rect) | |||
rect.adjust(1, 1, -1, 0) | |||
painter.drawTiledPixmap(rect, canvas.theme.box_header_pixmap, rect.topLeft()) | |||
# Draw text | |||
painter.setFont(self.m_font_name) | |||
@@ -2933,14 +2787,14 @@ class CanvasIcon(QGraphicsSvgItem): | |||
return self.p_size | |||
def paint(self, painter, option, widget): | |||
if self.m_renderer: | |||
painter.save() | |||
painter.setRenderHint(QPainter.Antialiasing, False) | |||
painter.setRenderHint(QPainter.TextAntialiasing, False) | |||
self.m_renderer.render(painter, self.p_size) | |||
painter.restore() | |||
else: | |||
QGraphicsSvgItem.paint(self, painter, option, widget) | |||
if not self.m_renderer: | |||
return QGraphicsSvgItem.paint(self, painter, option, widget) | |||
painter.save() | |||
painter.setRenderHint(QPainter.Antialiasing, False) | |||
painter.setRenderHint(QPainter.TextAntialiasing, False) | |||
self.m_renderer.render(painter, self.p_size) | |||
painter.restore() | |||
# ------------------------------------------------------------------------------ | |||
# canvasportglow.cpp | |||
@@ -118,9 +118,9 @@ class Theme(object): | |||
self.port_parameter_text = self.port_text | |||
self.port_parameter_text_sel = self.port_text | |||
self.port_height = 15 | |||
self.port_height = 16 | |||
self.port_offset = 0 | |||
self.port_spacing = 3 | |||
self.port_spacing = 2 | |||
self.port_spacingT = 2 | |||
# Lines | |||
@@ -200,9 +200,9 @@ class Theme(object): | |||
self.port_parameter_text = self.port_text | |||
self.port_parameter_text_sel = self.port_text | |||
self.port_height = 11 | |||
self.port_height = 12 | |||
self.port_offset = 0 | |||
self.port_spacing = 2 | |||
self.port_spacing = 1 | |||
self.port_spacingT = 1 | |||
# Lines | |||
@@ -282,9 +282,9 @@ class Theme(object): | |||
self.port_parameter_text = self.port_text | |||
self.port_parameter_text_sel = self.port_text | |||
self.port_height = 15 | |||
self.port_height = 16 | |||
self.port_offset = 0 | |||
self.port_spacing = 3 | |||
self.port_spacing = 2 | |||
self.port_spacingT = 2 | |||
# Lines | |||
@@ -450,9 +450,9 @@ class Theme(object): | |||
self.port_parameter_text_sel = self.port_parameter_pen_sel | |||
# missing, ports 2 | |||
self.port_height = 19 | |||
self.port_height = 21 | |||
self.port_offset = 1 | |||
self.port_spacing = 5 | |||
self.port_spacing = 3 | |||
self.port_spacingT = 0 | |||
# Lines | |||
@@ -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 | |||
@@ -20,7 +20,7 @@ | |||
#include "CarlaRingBuffer.hpp" | |||
#define CARLA_PLUGIN_BRIDGE_API_VERSION 4 | |||
#define CARLA_PLUGIN_BRIDGE_API_VERSION 5 | |||
// ------------------------------------------------------------------------------------------------------------------- | |||
@@ -37,7 +37,7 @@ enum PluginBridgeRtClientOpcode { | |||
kPluginBridgeRtClientControlEventAllSoundOff, // uint/frame, byte/chan | |||
kPluginBridgeRtClientControlEventAllNotesOff, // uint/frame, byte/chan | |||
kPluginBridgeRtClientMidiEvent, // uint/frame, byte/port, byte/size, byte[]/data | |||
kPluginBridgeRtClientProcess, | |||
kPluginBridgeRtClientProcess, // uint/frames | |||
kPluginBridgeRtClientQuit | |||
}; | |||
@@ -134,12 +134,13 @@ struct BridgeSemaphore { | |||
}; | |||
}; | |||
// needs to be 64bit aligned | |||
// NOTE: needs to be 64bit aligned | |||
struct BridgeTimeInfo { | |||
uint64_t playing; | |||
uint64_t frame; | |||
uint64_t usecs; | |||
uint32_t validFlags; | |||
uint32_t unused; | |||
// bbt | |||
int32_t bar, beat; | |||
float beatsPerBar, beatType; | |||
@@ -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 | |||