Signed-off-by: falkTX <falktx@falktx.com>pull/1689/head
@@ -28,6 +28,7 @@ | |||||
#include "CarlaLv2Utils.hpp" | #include "CarlaLv2Utils.hpp" | ||||
#include "CarlaVst2Utils.hpp" | #include "CarlaVst2Utils.hpp" | ||||
#include "CarlaVst3Utils.hpp" | #include "CarlaVst3Utils.hpp" | ||||
#include "CarlaClapUtils.hpp" | |||||
#ifdef CARLA_OS_MAC | #ifdef CARLA_OS_MAC | ||||
# include "CarlaMacUtils.cpp" | # include "CarlaMacUtils.cpp" | ||||
@@ -84,17 +85,17 @@ CARLA_BACKEND_USE_NAMESPACE | |||||
// ------------------------------------------------------------------------------------------------------------------- | // ------------------------------------------------------------------------------------------------------------------- | ||||
// Dummy values to test plugins with | // Dummy values to test plugins with | ||||
static const uint32_t kBufferSize = 512; | |||||
static const double kSampleRate = 44100.0; | |||||
static const int32_t kSampleRatei = 44100; | |||||
static const float kSampleRatef = 44100.0f; | |||||
static constexpr const uint32_t kBufferSize = 512; | |||||
static constexpr const double kSampleRate = 44100.0; | |||||
static constexpr const int32_t kSampleRatei = 44100; | |||||
static constexpr const float kSampleRatef = 44100.0f; | |||||
// ------------------------------------------------------------------------------------------------------------------- | // ------------------------------------------------------------------------------------------------------------------- | ||||
// Don't print ELF/EXE related errors since discovery can find multi-architecture binaries | // Don't print ELF/EXE related errors since discovery can find multi-architecture binaries | ||||
static void print_lib_error(const char* const filename) | static void print_lib_error(const char* const filename) | ||||
{ | { | ||||
const char* const error(lib_error(filename)); | |||||
const char* const error = lib_error(filename); | |||||
if (error != nullptr && | if (error != nullptr && | ||||
std::strstr(error, "wrong ELF class") == nullptr && | std::strstr(error, "wrong ELF class") == nullptr && | ||||
@@ -150,25 +151,25 @@ static void do_cached_check(const PluginType type) | |||||
break; | break; | ||||
} | } | ||||
# ifdef USING_JUCE | |||||
#ifdef USING_JUCE | |||||
if (type == PLUGIN_AU) | if (type == PLUGIN_AU) | ||||
CarlaJUCE::initialiseJuce_GUI(); | CarlaJUCE::initialiseJuce_GUI(); | ||||
# endif | |||||
#endif | |||||
const uint count = carla_get_cached_plugin_count(type, plugPath); | const uint count = carla_get_cached_plugin_count(type, plugPath); | ||||
for (uint i=0; i<count; ++i) | for (uint i=0; i<count; ++i) | ||||
{ | { | ||||
const CarlaCachedPluginInfo* pinfo(carla_get_cached_plugin_info(type, i)); | |||||
const CarlaCachedPluginInfo* pinfo = carla_get_cached_plugin_info(type, i); | |||||
CARLA_SAFE_ASSERT_CONTINUE(pinfo != nullptr); | CARLA_SAFE_ASSERT_CONTINUE(pinfo != nullptr); | ||||
print_cached_plugin(pinfo); | print_cached_plugin(pinfo); | ||||
} | } | ||||
# ifdef USING_JUCE | |||||
#ifdef USING_JUCE | |||||
if (type == PLUGIN_AU) | if (type == PLUGIN_AU) | ||||
CarlaJUCE::shutdownJuce_GUI(); | CarlaJUCE::shutdownJuce_GUI(); | ||||
# endif | |||||
#endif | |||||
} | } | ||||
#endif | #endif | ||||
@@ -1845,6 +1846,285 @@ static void do_vst3_check(lib_t& libHandle, const char* const filename, const bo | |||||
} | } | ||||
#endif // ! USING_JUCE_FOR_VST3 | #endif // ! USING_JUCE_FOR_VST3 | ||||
struct carla_clap_host : clap_host_t { | |||||
carla_clap_host() | |||||
{ | |||||
clap_version = CLAP_VERSION; | |||||
host_data = this; | |||||
name = "Carla-Discovery"; | |||||
vendor = "falkTX"; | |||||
url = "https://kx.studio/carla"; | |||||
version = CARLA_VERSION_STRING; | |||||
get_extension = carla_get_extension; | |||||
request_restart = carla_request_restart; | |||||
request_process = carla_request_process; | |||||
request_callback = carla_request_callback; | |||||
} | |||||
static const void* carla_get_extension(const clap_host_t*, const char*) { return nullptr; } | |||||
static void carla_request_restart(const clap_host_t*) {} | |||||
static void carla_request_process(const clap_host_t*) {} | |||||
static void carla_request_callback(const clap_host_t*) {} | |||||
}; | |||||
static void do_clap_check(lib_t& libHandle, const char* const filename, const bool doInit) | |||||
{ | |||||
const clap_plugin_entry_t* const entry = lib_symbol<const clap_plugin_entry_t*>(libHandle, "clap_entry"); | |||||
// ensure entry points are available | |||||
if (entry == nullptr || entry->init == nullptr || entry->deinit == nullptr || entry->get_factory == nullptr) | |||||
{ | |||||
DISCOVERY_OUT("error", "Not a CLAP plugin"); | |||||
return; | |||||
} | |||||
// ensure compatible version | |||||
if (!clap_version_is_compatible(entry->clap_version)) | |||||
{ | |||||
DISCOVERY_OUT("error", "Incompatible CLAP plugin"); | |||||
return; | |||||
} | |||||
const water::String pluginPath(water::File(filename).getParentDirectory().getFullPathName()); | |||||
entry->init(pluginPath.toRawUTF8()); | |||||
const clap_plugin_factory_t* const factory = static_cast<const clap_plugin_factory_t*>( | |||||
entry->get_factory(CLAP_PLUGIN_FACTORY_ID)); | |||||
CARLA_SAFE_ASSERT_RETURN(factory != nullptr | |||||
&& factory->get_plugin_count != nullptr | |||||
&& factory->get_plugin_descriptor != nullptr | |||||
&& factory->create_plugin != nullptr, entry->deinit()); | |||||
if (const uint32_t count = factory->get_plugin_count(factory)) | |||||
{ | |||||
const carla_clap_host host; | |||||
for (uint32_t i=0; i<count; ++i) | |||||
{ | |||||
const clap_plugin_descriptor_t* const desc = factory->get_plugin_descriptor(factory, i); | |||||
CARLA_SAFE_ASSERT_CONTINUE(desc != nullptr); | |||||
const clap_plugin_t* const plugin = factory->create_plugin(factory, &host, desc->id); | |||||
CARLA_SAFE_ASSERT_CONTINUE(plugin != nullptr); | |||||
uint hints = 0x0; | |||||
uint audioIns = 0; | |||||
uint audioOuts = 0; | |||||
// uint audioTotal = 0; | |||||
uint midiIns = 0; | |||||
uint midiOuts = 0; | |||||
// uint midiTotal = 0; | |||||
uint parametersIns = 0; | |||||
uint parametersOuts = 0; | |||||
// uint parametersTotal = 0; | |||||
PluginCategory category = PLUGIN_CATEGORY_NONE; | |||||
const clap_plugin_audio_ports_t* const audioPorts = static_cast<const clap_plugin_audio_ports_t*>( | |||||
plugin->get_extension(plugin, CLAP_EXT_AUDIO_PORTS)); | |||||
const clap_plugin_note_ports_t* const notePorts = static_cast<const clap_plugin_note_ports_t*>( | |||||
plugin->get_extension(plugin, CLAP_EXT_NOTE_PORTS)); | |||||
const clap_plugin_params_t* const params = static_cast<const clap_plugin_params_t*>( | |||||
plugin->get_extension(plugin, CLAP_EXT_PARAMS)); | |||||
if (audioPorts != nullptr) | |||||
{ | |||||
clap_audio_port_info_t info; | |||||
const uint32_t inPorts = audioPorts->count(plugin, true); | |||||
for (uint32_t j=0; j<inPorts; ++j) | |||||
{ | |||||
if (!audioPorts->get(plugin, j, true, &info)) | |||||
break; | |||||
audioIns += info.channel_count; | |||||
} | |||||
const uint32_t outPorts = audioPorts->count(plugin, false); | |||||
for (uint32_t j=0; j<outPorts; ++j) | |||||
{ | |||||
if (!audioPorts->get(plugin, j, false, &info)) | |||||
break; | |||||
audioOuts += info.channel_count; | |||||
} | |||||
// audioTotal = audioIns + audioOuts; | |||||
} | |||||
if (notePorts != nullptr) | |||||
{ | |||||
clap_note_port_info_t info; | |||||
const uint32_t inPorts = notePorts->count(plugin, true); | |||||
for (uint32_t j=0; j<inPorts; ++j) | |||||
{ | |||||
if (!notePorts->get(plugin, j, true, &info)) | |||||
break; | |||||
if (info.supported_dialects & CLAP_NOTE_DIALECT_MIDI) | |||||
++midiIns; | |||||
} | |||||
const uint32_t outPorts = notePorts->count(plugin, false); | |||||
for (uint32_t j=0; j<outPorts; ++j) | |||||
{ | |||||
if (!notePorts->get(plugin, j, false, &info)) | |||||
break; | |||||
if (info.supported_dialects & CLAP_NOTE_DIALECT_MIDI) | |||||
++midiOuts; | |||||
} | |||||
// midiTotal = midiIns + midiOuts; | |||||
} | |||||
if (params != nullptr) | |||||
{ | |||||
clap_param_info_t info; | |||||
const uint32_t numParams = params->count(plugin); | |||||
for (uint32_t j=0; j<numParams; ++j) | |||||
{ | |||||
if (!params->get_info(plugin, j, &info)) | |||||
break; | |||||
if (info.flags & (CLAP_PARAM_IS_HIDDEN|CLAP_PARAM_IS_BYPASS)) | |||||
continue; | |||||
if (info.flags & CLAP_PARAM_IS_READONLY) | |||||
++parametersOuts; | |||||
else | |||||
++parametersIns; | |||||
} | |||||
// parametersTotal = parametersIns + parametersOuts; | |||||
} | |||||
if (desc->features != nullptr) | |||||
{ | |||||
// 1st pass for main categories | |||||
for (uint32_t j=0; desc->features[j] != nullptr; ++j) | |||||
{ | |||||
if (std::strcmp(desc->features[j], CLAP_PLUGIN_FEATURE_INSTRUMENT) == 0) | |||||
{ | |||||
category = PLUGIN_CATEGORY_SYNTH; | |||||
break; | |||||
} | |||||
if (std::strcmp(desc->features[j], CLAP_PLUGIN_FEATURE_NOTE_EFFECT) == 0) | |||||
{ | |||||
category = PLUGIN_CATEGORY_UTILITY; | |||||
break; | |||||
} | |||||
if (std::strcmp(desc->features[j], CLAP_PLUGIN_FEATURE_ANALYZER) == 0) | |||||
{ | |||||
category = PLUGIN_CATEGORY_UTILITY; | |||||
break; | |||||
} | |||||
} | |||||
// 2nd pass for FX sub categories | |||||
if (category == PLUGIN_CATEGORY_NONE) | |||||
{ | |||||
/* | |||||
#define CLAP_PLUGIN_FEATURE_DEESSER "de-esser" | |||||
#define CLAP_PLUGIN_FEATURE_PHASE_VOCODER "phase-vocoder" | |||||
#define CLAP_PLUGIN_FEATURE_GRANULAR "granular" | |||||
#define CLAP_PLUGIN_FEATURE_FREQUENCY_SHIFTER "frequency-shifter" | |||||
#define CLAP_PLUGIN_FEATURE_PITCH_SHIFTER "pitch-shifter" | |||||
#define CLAP_PLUGIN_FEATURE_TREMOLO "tremolo" | |||||
#define CLAP_PLUGIN_FEATURE_GLITCH "glitch" | |||||
*/ | |||||
for (uint32_t j=0; desc->features[j] != nullptr; ++j) | |||||
{ | |||||
if (std::strcmp(desc->features[j], CLAP_PLUGIN_FEATURE_DELAY) == 0 || | |||||
std::strcmp(desc->features[j], CLAP_PLUGIN_FEATURE_REVERB) == 0) | |||||
{ | |||||
category = PLUGIN_CATEGORY_DELAY; | |||||
break; | |||||
} | |||||
if (std::strcmp(desc->features[j], CLAP_PLUGIN_FEATURE_EQUALIZER) == 0) | |||||
{ | |||||
category = PLUGIN_CATEGORY_EQ; | |||||
break; | |||||
} | |||||
if (std::strcmp(desc->features[j], CLAP_PLUGIN_FEATURE_FILTER) == 0) | |||||
{ | |||||
category = PLUGIN_CATEGORY_FILTER; | |||||
break; | |||||
} | |||||
if (std::strcmp(desc->features[j], CLAP_PLUGIN_FEATURE_DISTORTION) == 0) | |||||
{ | |||||
category = PLUGIN_CATEGORY_DISTORTION; | |||||
break; | |||||
} | |||||
if (std::strcmp(desc->features[j], CLAP_PLUGIN_FEATURE_COMPRESSOR) == 0 || | |||||
std::strcmp(desc->features[j], CLAP_PLUGIN_FEATURE_LIMITER) == 0 || | |||||
std::strcmp(desc->features[j], CLAP_PLUGIN_FEATURE_MASTERING) == 0 || | |||||
std::strcmp(desc->features[j], CLAP_PLUGIN_FEATURE_MIXING) == 0 || | |||||
std::strcmp(desc->features[j], CLAP_PLUGIN_FEATURE_TRANSIENT_SHAPER) == 0) | |||||
{ | |||||
category = PLUGIN_CATEGORY_DYNAMICS; | |||||
break; | |||||
} | |||||
if (std::strcmp(desc->features[j], CLAP_PLUGIN_FEATURE_CHORUS) == 0 || | |||||
std::strcmp(desc->features[j], CLAP_PLUGIN_FEATURE_FLANGER) == 0 || | |||||
std::strcmp(desc->features[j], CLAP_PLUGIN_FEATURE_PHASER) == 0 | |||||
) | |||||
{ | |||||
category = PLUGIN_CATEGORY_MODULATOR; | |||||
break; | |||||
} | |||||
if (std::strcmp(desc->features[j], CLAP_PLUGIN_FEATURE_PITCH_CORRECTION) == 0 || | |||||
std::strcmp(desc->features[j], CLAP_PLUGIN_FEATURE_RESTORATION) == 0 || | |||||
std::strcmp(desc->features[j], CLAP_PLUGIN_FEATURE_UTILITY) == 0 | |||||
) | |||||
{ | |||||
category = PLUGIN_CATEGORY_UTILITY; | |||||
break; | |||||
} | |||||
} | |||||
category = PLUGIN_CATEGORY_OTHER; | |||||
} | |||||
} | |||||
if (doInit) | |||||
{ | |||||
// ----------------------------------------------------------------------- | |||||
// start crash-free plugin test | |||||
plugin->init(plugin); | |||||
// TODO | |||||
// end crash-free plugin test | |||||
// ----------------------------------------------------------------------- | |||||
} | |||||
plugin->destroy(plugin); | |||||
DISCOVERY_OUT("init", "-----------"); | |||||
DISCOVERY_OUT("build", BINARY_NATIVE); | |||||
DISCOVERY_OUT("hints", hints); | |||||
DISCOVERY_OUT("category", getPluginCategoryAsString(category)); | |||||
DISCOVERY_OUT("name", desc->name); | |||||
DISCOVERY_OUT("label", desc->id); | |||||
DISCOVERY_OUT("maker", desc->vendor); | |||||
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", "------------"); | |||||
} | |||||
} | |||||
entry->deinit(); | |||||
} | |||||
#ifdef USING_JUCE | #ifdef USING_JUCE | ||||
// ------------------------------------------------------------------------------------------------------------------- | // ------------------------------------------------------------------------------------------------------------------- | ||||
// find all available plugin audio ports | // find all available plugin audio ports | ||||
@@ -2200,6 +2480,7 @@ int main(int argc, char* argv[]) | |||||
case PLUGIN_LADSPA: | case PLUGIN_LADSPA: | ||||
case PLUGIN_DSSI: | case PLUGIN_DSSI: | ||||
case PLUGIN_VST2: | case PLUGIN_VST2: | ||||
case PLUGIN_CLAP: | |||||
openLib = true; | openLib = true; | ||||
break; | break; | ||||
case PLUGIN_VST3: | case PLUGIN_VST3: | ||||
@@ -2294,6 +2575,7 @@ int main(int argc, char* argv[]) | |||||
case PLUGIN_DSSI: | case PLUGIN_DSSI: | ||||
case PLUGIN_VST2: | case PLUGIN_VST2: | ||||
case PLUGIN_VST3: | case PLUGIN_VST3: | ||||
case PLUGIN_CLAP: | |||||
removeFileFromQuarantine(filename); | removeFileFromQuarantine(filename); | ||||
break; | break; | ||||
default: | default: | ||||
@@ -2351,6 +2633,10 @@ int main(int argc, char* argv[]) | |||||
break; | break; | ||||
#endif | #endif | ||||
case PLUGIN_CLAP: | |||||
do_clap_check(handle, filename, doInit); | |||||
break; | |||||
case PLUGIN_DLS: | case PLUGIN_DLS: | ||||
case PLUGIN_GIG: | case PLUGIN_GIG: | ||||
case PLUGIN_SF2: | case PLUGIN_SF2: | ||||
@@ -231,6 +231,7 @@ CARLA_KEY_PATHS_DSSI = "Paths/DSSI" | |||||
CARLA_KEY_PATHS_LV2 = "Paths/LV2" | CARLA_KEY_PATHS_LV2 = "Paths/LV2" | ||||
CARLA_KEY_PATHS_VST2 = "Paths/VST2" | CARLA_KEY_PATHS_VST2 = "Paths/VST2" | ||||
CARLA_KEY_PATHS_VST3 = "Paths/VST3" | CARLA_KEY_PATHS_VST3 = "Paths/VST3" | ||||
CARLA_KEY_PATHS_CLAP = "Paths/CLAP" | |||||
CARLA_KEY_PATHS_SF2 = "Paths/SF2" | CARLA_KEY_PATHS_SF2 = "Paths/SF2" | ||||
CARLA_KEY_PATHS_SFZ = "Paths/SFZ" | CARLA_KEY_PATHS_SFZ = "Paths/SFZ" | ||||
CARLA_KEY_PATHS_JSFX = "Paths/JSFX" | CARLA_KEY_PATHS_JSFX = "Paths/JSFX" | ||||
@@ -351,6 +352,7 @@ DEFAULT_DSSI_PATH = "" | |||||
DEFAULT_LV2_PATH = "" | DEFAULT_LV2_PATH = "" | ||||
DEFAULT_VST2_PATH = "" | DEFAULT_VST2_PATH = "" | ||||
DEFAULT_VST3_PATH = "" | DEFAULT_VST3_PATH = "" | ||||
DEFAULT_CLAP_PATH = "" | |||||
DEFAULT_SF2_PATH = "" | DEFAULT_SF2_PATH = "" | ||||
DEFAULT_SFZ_PATH = "" | DEFAULT_SFZ_PATH = "" | ||||
DEFAULT_JSFX_PATH = "" | DEFAULT_JSFX_PATH = "" | ||||
@@ -399,6 +401,9 @@ if WINDOWS: | |||||
DEFAULT_VST3_PATH = COMMONPROGRAMFILES + "\\VST3" | DEFAULT_VST3_PATH = COMMONPROGRAMFILES + "\\VST3" | ||||
DEFAULT_VST3_PATH += ";" + LOCALAPPDATA + "\\Programs\\Common\\VST3" | DEFAULT_VST3_PATH += ";" + LOCALAPPDATA + "\\Programs\\Common\\VST3" | ||||
DEFAULT_CLAP_PATH = COMMONPROGRAMFILES + "\\CLAP" | |||||
DEFAULT_CLAP_PATH += ";" + LOCALAPPDATA + "\\Programs\\Common\\CLAP" | |||||
DEFAULT_SF2_PATH = APPDATA + "\\SF2" | DEFAULT_SF2_PATH = APPDATA + "\\SF2" | ||||
DEFAULT_SFZ_PATH = APPDATA + "\\SFZ" | DEFAULT_SFZ_PATH = APPDATA + "\\SFZ" | ||||
@@ -411,6 +416,7 @@ if WINDOWS: | |||||
if COMMONPROGRAMFILESx86: | if COMMONPROGRAMFILESx86: | ||||
DEFAULT_VST3_PATH += COMMONPROGRAMFILESx86 + "\\VST3" | DEFAULT_VST3_PATH += COMMONPROGRAMFILESx86 + "\\VST3" | ||||
DEFAULT_CLAP_PATH += COMMONPROGRAMFILESx86 + "\\CLAP" | |||||
elif HAIKU: | elif HAIKU: | ||||
splitter = ":" | splitter = ":" | ||||
@@ -432,6 +438,9 @@ elif HAIKU: | |||||
DEFAULT_VST3_PATH = HOME + "/.vst3" | DEFAULT_VST3_PATH = HOME + "/.vst3" | ||||
DEFAULT_VST3_PATH += ":/system/add-ons/media/vst3plugins" | DEFAULT_VST3_PATH += ":/system/add-ons/media/vst3plugins" | ||||
DEFAULT_CLAP_PATH = HOME + "/.clap" | |||||
DEFAULT_CLAP_PATH += ":/system/add-ons/media/clapplugins" | |||||
elif MACOS: | elif MACOS: | ||||
splitter = ":" | splitter = ":" | ||||
@@ -450,6 +459,9 @@ elif MACOS: | |||||
DEFAULT_VST3_PATH = HOME + "/Library/Audio/Plug-Ins/VST3" | DEFAULT_VST3_PATH = HOME + "/Library/Audio/Plug-Ins/VST3" | ||||
DEFAULT_VST3_PATH += ":/Library/Audio/Plug-Ins/VST3" | DEFAULT_VST3_PATH += ":/Library/Audio/Plug-Ins/VST3" | ||||
DEFAULT_CLAP_PATH = HOME + "/Library/Audio/Plug-Ins/CLAP" | |||||
DEFAULT_CLAP_PATH += ":/Library/Audio/Plug-Ins/CLAP" | |||||
DEFAULT_JSFX_PATH = HOME + "/Library/Application Support/REAPER/Effects" | DEFAULT_JSFX_PATH = HOME + "/Library/Application Support/REAPER/Effects" | ||||
#DEFAULT_JSFX_PATH += ":/Applications/REAPER.app/Contents/InstallFiles/Effects" | #DEFAULT_JSFX_PATH += ":/Applications/REAPER.app/Contents/InstallFiles/Effects" | ||||
@@ -482,6 +494,10 @@ else: | |||||
DEFAULT_VST3_PATH += ":/usr/lib/vst3" | DEFAULT_VST3_PATH += ":/usr/lib/vst3" | ||||
DEFAULT_VST3_PATH += ":/usr/local/lib/vst3" | DEFAULT_VST3_PATH += ":/usr/local/lib/vst3" | ||||
DEFAULT_CLAP_PATH = HOME + "/.clap" | |||||
DEFAULT_CLAP_PATH += ":/usr/lib/clap" | |||||
DEFAULT_CLAP_PATH += ":/usr/local/lib/clap" | |||||
DEFAULT_SF2_PATH = HOME + "/.sounds/sf2" | DEFAULT_SF2_PATH = HOME + "/.sounds/sf2" | ||||
DEFAULT_SF2_PATH += ":" + HOME + "/.sounds/sf3" | DEFAULT_SF2_PATH += ":" + HOME + "/.sounds/sf3" | ||||
DEFAULT_SF2_PATH += ":/usr/share/sounds/sf2" | DEFAULT_SF2_PATH += ":/usr/share/sounds/sf2" | ||||
@@ -503,10 +519,12 @@ if not WINDOWS: | |||||
if os.path.exists(winePrefix): | if os.path.exists(winePrefix): | ||||
DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files/VstPlugins" | DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files/VstPlugins" | ||||
DEFAULT_VST3_PATH += ":" + winePrefix + "/drive_c/Program Files/Common Files/VST3" | DEFAULT_VST3_PATH += ":" + winePrefix + "/drive_c/Program Files/Common Files/VST3" | ||||
DEFAULT_CLAP_PATH += ":" + winePrefix + "/drive_c/Program Files/Common Files/CLAP" | |||||
if kIs64bit and os.path.exists(winePrefix + "/drive_c/Program Files (x86)"): | if kIs64bit and os.path.exists(winePrefix + "/drive_c/Program Files (x86)"): | ||||
DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files (x86)/VstPlugins" | DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files (x86)/VstPlugins" | ||||
DEFAULT_VST3_PATH += ":" + winePrefix + "/drive_c/Program Files (x86)/Common Files/VST3" | DEFAULT_VST3_PATH += ":" + winePrefix + "/drive_c/Program Files (x86)/Common Files/VST3" | ||||
DEFAULT_CLAP_PATH += ":" + winePrefix + "/drive_c/Program Files (x86)/Common Files/CLAP" | |||||
del winePrefix | del winePrefix | ||||
@@ -539,6 +557,7 @@ if readEnvVars: | |||||
CARLA_DEFAULT_LV2_PATH = os.getenv("LV2_PATH", DEFAULT_LV2_PATH).split(splitter) | CARLA_DEFAULT_LV2_PATH = os.getenv("LV2_PATH", DEFAULT_LV2_PATH).split(splitter) | ||||
CARLA_DEFAULT_VST2_PATH = os.getenv("VST_PATH", DEFAULT_VST2_PATH).split(splitter) | CARLA_DEFAULT_VST2_PATH = os.getenv("VST_PATH", DEFAULT_VST2_PATH).split(splitter) | ||||
CARLA_DEFAULT_VST3_PATH = os.getenv("VST3_PATH", DEFAULT_VST3_PATH).split(splitter) | CARLA_DEFAULT_VST3_PATH = os.getenv("VST3_PATH", DEFAULT_VST3_PATH).split(splitter) | ||||
CARLA_DEFAULT_CLAP_PATH = os.getenv("CLAP_PATH", DEFAULT_CLAP_PATH).split(splitter) | |||||
CARLA_DEFAULT_SF2_PATH = os.getenv("SF2_PATH", DEFAULT_SF2_PATH).split(splitter) | CARLA_DEFAULT_SF2_PATH = os.getenv("SF2_PATH", DEFAULT_SF2_PATH).split(splitter) | ||||
CARLA_DEFAULT_SFZ_PATH = os.getenv("SFZ_PATH", DEFAULT_SFZ_PATH).split(splitter) | CARLA_DEFAULT_SFZ_PATH = os.getenv("SFZ_PATH", DEFAULT_SFZ_PATH).split(splitter) | ||||
CARLA_DEFAULT_JSFX_PATH = os.getenv("JSFX_PATH", DEFAULT_JSFX_PATH).split(splitter) | CARLA_DEFAULT_JSFX_PATH = os.getenv("JSFX_PATH", DEFAULT_JSFX_PATH).split(splitter) | ||||
@@ -549,6 +568,7 @@ else: | |||||
CARLA_DEFAULT_LV2_PATH = DEFAULT_LV2_PATH.split(splitter) | CARLA_DEFAULT_LV2_PATH = DEFAULT_LV2_PATH.split(splitter) | ||||
CARLA_DEFAULT_VST2_PATH = DEFAULT_VST2_PATH.split(splitter) | CARLA_DEFAULT_VST2_PATH = DEFAULT_VST2_PATH.split(splitter) | ||||
CARLA_DEFAULT_VST3_PATH = DEFAULT_VST3_PATH.split(splitter) | CARLA_DEFAULT_VST3_PATH = DEFAULT_VST3_PATH.split(splitter) | ||||
CARLA_DEFAULT_CLAP_PATH = DEFAULT_CLAP_PATH.split(splitter) | |||||
CARLA_DEFAULT_SF2_PATH = DEFAULT_SF2_PATH.split(splitter) | CARLA_DEFAULT_SF2_PATH = DEFAULT_SF2_PATH.split(splitter) | ||||
CARLA_DEFAULT_SFZ_PATH = DEFAULT_SFZ_PATH.split(splitter) | CARLA_DEFAULT_SFZ_PATH = DEFAULT_SFZ_PATH.split(splitter) | ||||
CARLA_DEFAULT_JSFX_PATH = DEFAULT_JSFX_PATH.split(splitter) | CARLA_DEFAULT_JSFX_PATH = DEFAULT_JSFX_PATH.split(splitter) | ||||
@@ -561,6 +581,7 @@ del DEFAULT_DSSI_PATH | |||||
del DEFAULT_LV2_PATH | del DEFAULT_LV2_PATH | ||||
del DEFAULT_VST2_PATH | del DEFAULT_VST2_PATH | ||||
del DEFAULT_VST3_PATH | del DEFAULT_VST3_PATH | ||||
del DEFAULT_CLAP_PATH | |||||
del DEFAULT_SF2_PATH | del DEFAULT_SF2_PATH | ||||
del DEFAULT_SFZ_PATH | del DEFAULT_SFZ_PATH | ||||
@@ -40,6 +40,7 @@ from carla_backend import ( | |||||
PLUGIN_SFZ, | PLUGIN_SFZ, | ||||
PLUGIN_VST2, | PLUGIN_VST2, | ||||
PLUGIN_VST3, | PLUGIN_VST3, | ||||
PLUGIN_CLAP, | |||||
) | ) | ||||
from carla_shared import ( | from carla_shared import ( | ||||
@@ -80,6 +81,15 @@ def findVST3Binaries(binPath): | |||||
return binaries | return binaries | ||||
def findCLAPBinaries(binPath): | |||||
binaries = [] | |||||
for root, dirs, files in os.walk(binPath): | |||||
for name in tuple(name for name in (files+dirs) if name.lower().endswith(".clap")): | |||||
binaries.append(os.path.join(root, name)) | |||||
return binaries | |||||
def findLV2Bundles(bundlePath): | def findLV2Bundles(bundlePath): | ||||
bundles = [] | bundles = [] | ||||
@@ -380,6 +390,9 @@ def checkPluginVST2(filename, tool, wineSettings=None): | |||||
def checkPluginVST3(filename, tool, wineSettings=None): | def checkPluginVST3(filename, tool, wineSettings=None): | ||||
return runCarlaDiscovery(PLUGIN_VST3, "VST3", filename, tool, wineSettings) | return runCarlaDiscovery(PLUGIN_VST3, "VST3", filename, tool, wineSettings) | ||||
def checkPluginCLAP(filename, tool, wineSettings=None): | |||||
return runCarlaDiscovery(PLUGIN_CLAP, "CLAP", filename, tool, wineSettings) | |||||
def checkFileSF2(filename, tool): | def checkFileSF2(filename, tool): | ||||
return runCarlaDiscovery(PLUGIN_SF2, "SF2", filename, tool) | return runCarlaDiscovery(PLUGIN_SF2, "SF2", filename, tool) | ||||
@@ -35,6 +35,7 @@ from carla_backend import ( | |||||
PLUGIN_LV2, | PLUGIN_LV2, | ||||
PLUGIN_SFZ, | PLUGIN_SFZ, | ||||
PLUGIN_VST2, | PLUGIN_VST2, | ||||
PLUGIN_CLAP, | |||||
) | ) | ||||
from carla_shared import ( | from carla_shared import ( | ||||
@@ -46,6 +47,7 @@ from carla_shared import ( | |||||
CARLA_DEFAULT_SFZ_PATH, | CARLA_DEFAULT_SFZ_PATH, | ||||
CARLA_DEFAULT_VST2_PATH, | CARLA_DEFAULT_VST2_PATH, | ||||
CARLA_DEFAULT_VST3_PATH, | CARLA_DEFAULT_VST3_PATH, | ||||
CARLA_DEFAULT_CLAP_PATH, | |||||
CARLA_DEFAULT_WINE_AUTO_PREFIX, | CARLA_DEFAULT_WINE_AUTO_PREFIX, | ||||
CARLA_DEFAULT_WINE_EXECUTABLE, | CARLA_DEFAULT_WINE_EXECUTABLE, | ||||
CARLA_DEFAULT_WINE_FALLBACK_PREFIX, | CARLA_DEFAULT_WINE_FALLBACK_PREFIX, | ||||
@@ -57,6 +59,7 @@ from carla_shared import ( | |||||
CARLA_KEY_PATHS_SFZ, | CARLA_KEY_PATHS_SFZ, | ||||
CARLA_KEY_PATHS_VST2, | CARLA_KEY_PATHS_VST2, | ||||
CARLA_KEY_PATHS_VST3, | CARLA_KEY_PATHS_VST3, | ||||
CARLA_KEY_PATHS_CLAP, | |||||
CARLA_KEY_WINE_AUTO_PREFIX, | CARLA_KEY_WINE_AUTO_PREFIX, | ||||
CARLA_KEY_WINE_EXECUTABLE, | CARLA_KEY_WINE_EXECUTABLE, | ||||
CARLA_KEY_WINE_FALLBACK_PREFIX, | CARLA_KEY_WINE_FALLBACK_PREFIX, | ||||
@@ -78,6 +81,7 @@ from .discovery import ( | |||||
checkPluginLADSPA, | checkPluginLADSPA, | ||||
checkPluginVST2, | checkPluginVST2, | ||||
checkPluginVST3, | checkPluginVST3, | ||||
checkPluginCLAP, | |||||
findBinaries, | findBinaries, | ||||
findFilenames, | findFilenames, | ||||
findMacVSTBundles, | findMacVSTBundles, | ||||
@@ -107,6 +111,7 @@ class SearchPluginsThread(QThread): | |||||
self.fCheckLV2 = False | self.fCheckLV2 = False | ||||
self.fCheckVST2 = False | self.fCheckVST2 = False | ||||
self.fCheckVST3 = False | self.fCheckVST3 = False | ||||
self.fCheckCLAP = False | |||||
self.fCheckAU = False | self.fCheckAU = False | ||||
self.fCheckSF2 = False | self.fCheckSF2 = False | ||||
self.fCheckSFZ = False | self.fCheckSFZ = False | ||||
@@ -152,13 +157,14 @@ class SearchPluginsThread(QThread): | |||||
self.fCheckWin32 = win32 | self.fCheckWin32 = win32 | ||||
self.fCheckWin64 = win64 | self.fCheckWin64 = win64 | ||||
def setSearchPluginTypes(self, ladspa: bool, dssi: bool, lv2: bool, vst2: bool, vst3: bool, au: bool, | |||||
sf2: bool, sfz: bool, jsfx: bool): | |||||
def setSearchPluginTypes(self, ladspa: bool, dssi: bool, lv2: bool, vst2: bool, vst3: bool, clap: bool, | |||||
au: bool, sf2: bool, sfz: bool, jsfx: bool): | |||||
self.fCheckLADSPA = ladspa | self.fCheckLADSPA = ladspa | ||||
self.fCheckDSSI = dssi | self.fCheckDSSI = dssi | ||||
self.fCheckLV2 = lv2 | self.fCheckLV2 = lv2 | ||||
self.fCheckVST2 = vst2 | self.fCheckVST2 = vst2 | ||||
self.fCheckVST3 = vst3 and (LINUX or MACOS or WINDOWS) | |||||
self.fCheckVST3 = vst3 | |||||
self.fCheckCLAP = clap | |||||
self.fCheckAU = au and MACOS | self.fCheckAU = au and MACOS | ||||
self.fCheckSF2 = sf2 | self.fCheckSF2 = sf2 | ||||
self.fCheckSFZ = sfz | self.fCheckSFZ = sfz | ||||
@@ -190,23 +196,18 @@ class SearchPluginsThread(QThread): | |||||
pluginCount += 1 | pluginCount += 1 | ||||
if self.fCheckVST3: | if self.fCheckVST3: | ||||
pluginCount += 1 | pluginCount += 1 | ||||
if self.fCheckCLAP: | |||||
pluginCount += 1 | |||||
# Increase count by the number of externally discoverable plugin types | # Increase count by the number of externally discoverable plugin types | ||||
if self.fCheckNative: | if self.fCheckNative: | ||||
self.fCurCount += pluginCount | self.fCurCount += pluginCount | ||||
# Linux, MacOS and Windows are the only VST3 supported OSes | |||||
if self.fCheckVST3 and not (LINUX or MACOS or WINDOWS): | |||||
self.fCurCount -= 1 | |||||
if self.fCheckPosix32: | if self.fCheckPosix32: | ||||
self.fCurCount += pluginCount | self.fCurCount += pluginCount | ||||
if self.fCheckVST3 and not (LINUX or MACOS): | |||||
self.fCurCount -= 1 | |||||
if self.fCheckPosix64: | if self.fCheckPosix64: | ||||
self.fCurCount += pluginCount | self.fCurCount += pluginCount | ||||
if self.fCheckVST3 and not (LINUX or MACOS): | |||||
self.fCurCount -= 1 | |||||
if self.fCheckWin32: | if self.fCheckWin32: | ||||
self.fCurCount += pluginCount | self.fCurCount += pluginCount | ||||
@@ -421,6 +422,43 @@ class SearchPluginsThread(QThread): | |||||
if not self.fContinueChecking: | if not self.fContinueChecking: | ||||
return | return | ||||
if self.fCheckCLAP: | |||||
if self.fCheckNative: | |||||
plugins = self._checkCLAP(self.fToolNative) | |||||
settingsDB.setValue("Plugins/CLAP_native", plugins) | |||||
if not self.fContinueChecking: | |||||
return | |||||
if self.fCheckPosix32: | |||||
plugins = self._checkCLAP(os.path.join(self.fPathBinaries, "carla-discovery-posix32")) | |||||
settingsDB.setValue("Plugins/CLAP_posix32", plugins) | |||||
if not self.fContinueChecking: | |||||
return | |||||
if self.fCheckPosix64: | |||||
plugins = self._checkCLAP(os.path.join(self.fPathBinaries, "carla-discovery-posix64")) | |||||
settingsDB.setValue("Plugins/CLAP_posix64", plugins) | |||||
if not self.fContinueChecking: | |||||
return | |||||
if self.fCheckWin32: | |||||
tool = os.path.join(self.fPathBinaries, "carla-discovery-win32.exe") | |||||
plugins = self._checkCLAP(tool, not WINDOWS) | |||||
settingsDB.setValue("Plugins/CLAP_win32", plugins) | |||||
if not self.fContinueChecking: | |||||
return | |||||
if self.fCheckWin64: | |||||
tool = os.path.join(self.fPathBinaries, "carla-discovery-win64.exe") | |||||
plugins = self._checkCLAP(tool, not WINDOWS) | |||||
settingsDB.setValue("Plugins/CLAP_win64", plugins) | |||||
if not self.fContinueChecking: | |||||
return | |||||
settingsDB.sync() | |||||
if not self.fContinueChecking: | |||||
return | |||||
if self.fCheckAU: | if self.fCheckAU: | ||||
if self.fCheckNative: | if self.fCheckNative: | ||||
plugins = self._checkCached(False) | plugins = self._checkCached(False) | ||||
@@ -619,6 +657,42 @@ class SearchPluginsThread(QThread): | |||||
self.fLastCheckValue += self.fCurPercentValue | self.fLastCheckValue += self.fCurPercentValue | ||||
return vst3Plugins | return vst3Plugins | ||||
def _checkCLAP(self, OS, tool, isWine=False): | |||||
clapBinaries = [] | |||||
clapPlugins = [] | |||||
self._pluginLook(self.fLastCheckValue, "CLAP plugins...") | |||||
settings = QSafeSettings("falkTX", "Carla2") | |||||
CLAP_PATH = settings.value(CARLA_KEY_PATHS_CLAP, CARLA_DEFAULT_CLAP_PATH, list) | |||||
del settings | |||||
for iPATH in CLAP_PATH: | |||||
binaries = findCLAPBinaries(iPATH) | |||||
for binary in binaries: | |||||
if binary not in clapBinaries: | |||||
clapBinaries.append(binary) | |||||
clapBinaries.sort() | |||||
if not self.fContinueChecking: | |||||
return clapPlugins | |||||
for i in range(len(clapBinaries)): | |||||
clap = clapBinaries[i] | |||||
percent = ( float(i) / len(clapBinaries) ) * self.fCurPercentValue | |||||
self._pluginLook(self.fLastCheckValue + percent, clap) | |||||
plugins = checkPluginCLAP(clap, tool, self.fWineSettings if isWine else None) | |||||
if plugins: | |||||
clapPlugins.append(plugins) | |||||
if not self.fContinueChecking: | |||||
break | |||||
self.fLastCheckValue += self.fCurPercentValue | |||||
return clapPlugins | |||||
def _checkAU(self, tool): | def _checkAU(self, tool): | ||||
auPlugins = [] | auPlugins = [] | ||||
@@ -685,6 +685,13 @@ | |||||
</property> | </property> | ||||
</widget> | </widget> | ||||
</item> | </item> | ||||
<item> | |||||
<widget class="QCheckBox" name="ch_clap"> | |||||
<property name="text"> | |||||
<string>CLAP</string> | |||||
</property> | |||||
</widget> | |||||
</item> | |||||
<item> | <item> | ||||
<widget class="QCheckBox" name="ch_au"> | <widget class="QCheckBox" name="ch_au"> | ||||
<property name="text"> | <property name="text"> | ||||
@@ -941,6 +948,7 @@ | |||||
<tabstop>ch_lv2</tabstop> | <tabstop>ch_lv2</tabstop> | ||||
<tabstop>ch_vst</tabstop> | <tabstop>ch_vst</tabstop> | ||||
<tabstop>ch_vst3</tabstop> | <tabstop>ch_vst3</tabstop> | ||||
<tabstop>ch_clap</tabstop> | |||||
<tabstop>ch_au</tabstop> | <tabstop>ch_au</tabstop> | ||||
<tabstop>ch_kits</tabstop> | <tabstop>ch_kits</tabstop> | ||||
<tabstop>ch_effects</tabstop> | <tabstop>ch_effects</tabstop> | ||||
@@ -166,10 +166,6 @@ class PluginRefreshW(QDialog): | |||||
self.ui.ico_posix32.setVisible(False) | self.ui.ico_posix32.setVisible(False) | ||||
self.ui.label_posix32.setVisible(False) | self.ui.label_posix32.setVisible(False) | ||||
if not (LINUX or hasWin32 or hasWin64): | |||||
self.ui.ch_vst3.setEnabled(False) | |||||
self.ui.ch_vst3.setVisible(False) | |||||
if MACOS: | if MACOS: | ||||
self.setWindowModality(Qt.WindowModal) | self.setWindowModality(Qt.WindowModal) | ||||
else: | else: | ||||
@@ -187,6 +183,7 @@ class PluginRefreshW(QDialog): | |||||
self.ui.ch_dssi.setEnabled(False) | self.ui.ch_dssi.setEnabled(False) | ||||
self.ui.ch_vst.setEnabled(False) | self.ui.ch_vst.setEnabled(False) | ||||
self.ui.ch_vst3.setEnabled(False) | self.ui.ch_vst3.setEnabled(False) | ||||
self.ui.ch_clap.setEnabled(False) | |||||
if not hasLoadedLv2Plugins: | if not hasLoadedLv2Plugins: | ||||
self.ui.lv2_restart_notice.hide() | self.ui.lv2_restart_notice.hide() | ||||
@@ -272,6 +269,7 @@ class PluginRefreshW(QDialog): | |||||
self.ui.ch_lv2.clicked.connect(self.slot_checkTools) | self.ui.ch_lv2.clicked.connect(self.slot_checkTools) | ||||
self.ui.ch_vst.clicked.connect(self.slot_checkTools) | self.ui.ch_vst.clicked.connect(self.slot_checkTools) | ||||
self.ui.ch_vst3.clicked.connect(self.slot_checkTools) | self.ui.ch_vst3.clicked.connect(self.slot_checkTools) | ||||
self.ui.ch_clap.clicked.connect(self.slot_checkTools) | |||||
self.ui.ch_au.clicked.connect(self.slot_checkTools) | self.ui.ch_au.clicked.connect(self.slot_checkTools) | ||||
self.ui.ch_sf2.clicked.connect(self.slot_checkTools) | self.ui.ch_sf2.clicked.connect(self.slot_checkTools) | ||||
self.ui.ch_sfz.clicked.connect(self.slot_checkTools) | self.ui.ch_sfz.clicked.connect(self.slot_checkTools) | ||||
@@ -304,6 +302,9 @@ class PluginRefreshW(QDialog): | |||||
check = settings.value("PluginDatabase/SearchVST3", True, bool) and self.ui.ch_vst3.isEnabled() | check = settings.value("PluginDatabase/SearchVST3", True, bool) and self.ui.ch_vst3.isEnabled() | ||||
self.ui.ch_vst3.setChecked(check) | self.ui.ch_vst3.setChecked(check) | ||||
check = settings.value("PluginDatabase/SearchCLAP", True, bool) and self.ui.ch_clap.isEnabled() | |||||
self.ui.ch_clap.setChecked(check) | |||||
if MACOS: | if MACOS: | ||||
check = settings.value("PluginDatabase/SearchAU", True, bool) and self.ui.ch_au.isEnabled() | check = settings.value("PluginDatabase/SearchAU", True, bool) and self.ui.ch_au.isEnabled() | ||||
else: | else: | ||||
@@ -346,6 +347,7 @@ class PluginRefreshW(QDialog): | |||||
settings.setValue("PluginDatabase/SearchLV2", self.ui.ch_lv2.isChecked()) | settings.setValue("PluginDatabase/SearchLV2", self.ui.ch_lv2.isChecked()) | ||||
settings.setValue("PluginDatabase/SearchVST2", self.ui.ch_vst.isChecked()) | settings.setValue("PluginDatabase/SearchVST2", self.ui.ch_vst.isChecked()) | ||||
settings.setValue("PluginDatabase/SearchVST3", self.ui.ch_vst3.isChecked()) | settings.setValue("PluginDatabase/SearchVST3", self.ui.ch_vst3.isChecked()) | ||||
settings.setValue("PluginDatabase/SearchCLAP", self.ui.ch_clap.isChecked()) | |||||
settings.setValue("PluginDatabase/SearchAU", self.ui.ch_au.isChecked()) | settings.setValue("PluginDatabase/SearchAU", self.ui.ch_au.isChecked()) | ||||
settings.setValue("PluginDatabase/SearchSF2", self.ui.ch_sf2.isChecked()) | settings.setValue("PluginDatabase/SearchSF2", self.ui.ch_sf2.isChecked()) | ||||
settings.setValue("PluginDatabase/SearchSFZ", self.ui.ch_sfz.isChecked()) | settings.setValue("PluginDatabase/SearchSFZ", self.ui.ch_sfz.isChecked()) | ||||
@@ -380,14 +382,14 @@ class PluginRefreshW(QDialog): | |||||
self.ui.ch_posix32.isChecked(), self.ui.ch_posix64.isChecked(), | self.ui.ch_posix32.isChecked(), self.ui.ch_posix64.isChecked(), | ||||
self.ui.ch_win32.isChecked(), self.ui.ch_win64.isChecked()) | self.ui.ch_win32.isChecked(), self.ui.ch_win64.isChecked()) | ||||
ladspa, dssi, lv2, vst, vst3, au, sf2, sfz, jsfx = (self.ui.ch_ladspa.isChecked(), self.ui.ch_dssi.isChecked(), | |||||
self.ui.ch_lv2.isChecked(), self.ui.ch_vst.isChecked(), | |||||
self.ui.ch_vst3.isChecked(), self.ui.ch_au.isChecked(), | |||||
self.ui.ch_sf2.isChecked(), self.ui.ch_sfz.isChecked(), | |||||
self.ui.ch_jsfx.isChecked()) | |||||
ladspa, dssi, lv2, vst, vst3, clap, au, sf2, sfz, jsfx = (self.ui.ch_ladspa.isChecked(), self.ui.ch_dssi.isChecked(), | |||||
self.ui.ch_lv2.isChecked(), self.ui.ch_vst.isChecked(), | |||||
self.ui.ch_vst3.isChecked(), self.ui.ch_clap.isChecked(), | |||||
self.ui.ch_au.isChecked(), self.ui.ch_sf2.isChecked(), | |||||
self.ui.ch_sfz.isChecked(), self.ui.ch_jsfx.isChecked()) | |||||
self.fThread.setSearchBinaryTypes(native, posix32, posix64, win32, win64) | self.fThread.setSearchBinaryTypes(native, posix32, posix64, win32, win64) | ||||
self.fThread.setSearchPluginTypes(ladspa, dssi, lv2, vst, vst3, au, sf2, sfz, jsfx) | |||||
self.fThread.setSearchPluginTypes(ladspa, dssi, lv2, vst, vst3, clap, au, sf2, sfz, jsfx) | |||||
self.fThread.start() | self.fThread.start() | ||||
# ----------------------------------------------------------------------------------------------------------------- | # ----------------------------------------------------------------------------------------------------------------- | ||||
@@ -406,9 +408,9 @@ class PluginRefreshW(QDialog): | |||||
enabled2 = bool(self.ui.ch_ladspa.isChecked() or self.ui.ch_dssi.isChecked() or | enabled2 = bool(self.ui.ch_ladspa.isChecked() or self.ui.ch_dssi.isChecked() or | ||||
self.ui.ch_lv2.isChecked() or self.ui.ch_vst.isChecked() or | self.ui.ch_lv2.isChecked() or self.ui.ch_vst.isChecked() or | ||||
self.ui.ch_vst3.isChecked() or self.ui.ch_au.isChecked() or | |||||
self.ui.ch_sf2.isChecked() or self.ui.ch_sfz.isChecked() or | |||||
self.ui.ch_jsfx.isChecked()) | |||||
self.ui.ch_vst3.isChecked() or self.ui.ch_clap.isChecked() or | |||||
self.ui.ch_au.isChecked() or self.ui.ch_sf2.isChecked() or | |||||
self.ui.ch_sfz.isChecked() or self.ui.ch_jsfx.isChecked()) | |||||
self.ui.b_start.setEnabled(enabled1 and enabled2) | self.ui.b_start.setEnabled(enabled1 and enabled2) | ||||
@@ -78,6 +78,13 @@ | |||||
</property> | </property> | ||||
</widget> | </widget> | ||||
</item> | </item> | ||||
<item> | |||||
<widget class="QCheckBox" name="ch_clap"> | |||||
<property name="text"> | |||||
<string>CLAP</string> | |||||
</property> | |||||
</widget> | |||||
</item> | |||||
<item> | <item> | ||||
<widget class="QCheckBox" name="ch_au"> | <widget class="QCheckBox" name="ch_au"> | ||||
<property name="text"> | <property name="text"> | ||||
@@ -0,0 +1,37 @@ | |||||
#pragma once | |||||
#include "private/std.h" | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#endif | |||||
// Sample code for reading a stereo buffer: | |||||
// | |||||
// bool isLeftConstant = (buffer->constant_mask & (1 << 0)) != 0; | |||||
// bool isRightConstant = (buffer->constant_mask & (1 << 1)) != 0; | |||||
// | |||||
// for (int i = 0; i < N; ++i) { | |||||
// float l = data32[0][isLeftConstant ? 0 : i]; | |||||
// float r = data32[1][isRightConstant ? 0 : i]; | |||||
// } | |||||
// | |||||
// Note: checking the constant mask is optional, and this implies that | |||||
// the buffer must be filled with the constant value. | |||||
// Rationale: if a buffer reader doesn't check the constant mask, then it may | |||||
// process garbage samples and in result, garbage samples may be transmitted | |||||
// to the audio interface with all the bad consequences it can have. | |||||
// | |||||
// The constant mask is a hint. | |||||
typedef struct clap_audio_buffer { | |||||
// Either data32 or data64 pointer will be set. | |||||
float **data32; | |||||
double **data64; | |||||
uint32_t channel_count; | |||||
uint32_t latency; // latency from/to the audio interface | |||||
uint64_t constant_mask; | |||||
} clap_audio_buffer_t; | |||||
#ifdef __cplusplus | |||||
} | |||||
#endif |
@@ -0,0 +1,68 @@ | |||||
#pragma once | |||||
#include "version.h" | |||||
#include "private/macros.h" | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#endif | |||||
// This interface is the entry point of the dynamic library. | |||||
// | |||||
// CLAP plugins standard search path: | |||||
// | |||||
// Linux | |||||
// - ~/.clap | |||||
// - /usr/lib/clap | |||||
// | |||||
// Windows | |||||
// - %CommonFilesFolder%/CLAP/ | |||||
// - %LOCALAPPDATA%/Programs/Common/CLAP/ | |||||
// | |||||
// MacOS | |||||
// - /Library/Audio/Plug-Ins/CLAP | |||||
// - ~/Library/Audio/Plug-Ins/CLAP | |||||
// | |||||
// In addition to the OS-specific default locations above, a CLAP host must query the environment | |||||
// for a CLAP_PATH variable, which is a list of directories formatted in the same manner as the host | |||||
// OS binary search path (PATH on Unix, separated by `:` and Path on Windows, separated by ';', as | |||||
// of this writing). | |||||
// | |||||
// Each directory should be recursively searched for files and/or bundles as appropriate in your OS | |||||
// ending with the extension `.clap`. | |||||
// | |||||
// Every method must be thread-safe. | |||||
typedef struct clap_plugin_entry { | |||||
clap_version_t clap_version; // initialized to CLAP_VERSION | |||||
// This function must be called first, and can only be called once. | |||||
// | |||||
// It should be as fast as possible, in order to perform a very quick scan of the plugin | |||||
// descriptors. | |||||
// | |||||
// It is forbidden to display graphical user interface in this call. | |||||
// It is forbidden to perform user interaction in this call. | |||||
// | |||||
// If the initialization depends upon expensive computation, maybe try to do them ahead of time | |||||
// and cache the result. | |||||
// | |||||
// If init() returns false, then the host must not call deinit() nor any other clap | |||||
// related symbols from the DSO. | |||||
bool (*init)(const char *plugin_path); | |||||
// No more calls into the DSO must be made after calling deinit(). | |||||
void (*deinit)(void); | |||||
// Get the pointer to a factory. See plugin-factory.h for an example. | |||||
// | |||||
// Returns null if the factory is not provided. | |||||
// The returned pointer must *not* be freed by the caller. | |||||
const void *(*get_factory)(const char *factory_id); | |||||
} clap_plugin_entry_t; | |||||
/* Entry point */ | |||||
CLAP_EXPORT extern const clap_plugin_entry_t clap_entry; | |||||
#ifdef __cplusplus | |||||
} | |||||
#endif |
@@ -0,0 +1,283 @@ | |||||
#pragma once | |||||
#include "private/std.h" | |||||
#include "fixedpoint.h" | |||||
#include "id.h" | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#endif | |||||
// event header | |||||
// must be the first attribute of the event | |||||
typedef struct clap_event_header { | |||||
uint32_t size; // event size including this header, eg: sizeof (clap_event_note) | |||||
uint32_t time; // sample offset within the buffer for this event | |||||
uint16_t space_id; // event space, see clap_host_event_registry | |||||
uint16_t type; // event type | |||||
uint32_t flags; // see clap_event_flags | |||||
} clap_event_header_t; | |||||
// The clap core event space | |||||
static const CLAP_CONSTEXPR uint16_t CLAP_CORE_EVENT_SPACE_ID = 0; | |||||
enum clap_event_flags { | |||||
// Indicate a live user event, for example a user turning a physical knob | |||||
// or playing a physical key. | |||||
CLAP_EVENT_IS_LIVE = 1 << 0, | |||||
// Indicate that the event should not be recorded. | |||||
// For example this is useful when a parameter changes because of a MIDI CC, | |||||
// because if the host records both the MIDI CC automation and the parameter | |||||
// automation there will be a conflict. | |||||
CLAP_EVENT_DONT_RECORD = 1 << 1, | |||||
}; | |||||
// Some of the following events overlap, a note on can be expressed with: | |||||
// - CLAP_EVENT_NOTE_ON | |||||
// - CLAP_EVENT_MIDI | |||||
// - CLAP_EVENT_MIDI2 | |||||
// | |||||
// The preferred way of sending a note event is to use CLAP_EVENT_NOTE_*. | |||||
// | |||||
// The same event must not be sent twice: it is forbidden to send a the same note on | |||||
// encoded with both CLAP_EVENT_NOTE_ON and CLAP_EVENT_MIDI. | |||||
// | |||||
// The plugins are encouraged to be able to handle note events encoded as raw midi or midi2, | |||||
// or implement clap_plugin_event_filter and reject raw midi and midi2 events. | |||||
enum { | |||||
// NOTE_ON and NOTE_OFF represent a key pressed and key released event, respectively. | |||||
// A NOTE_ON with a velocity of 0 is valid and should not be interpreted as a NOTE_OFF. | |||||
// | |||||
// NOTE_CHOKE is meant to choke the voice(s), like in a drum machine when a closed hihat | |||||
// chokes an open hihat. This event can be sent by the host to the plugin. Here are two use cases: | |||||
// - a plugin is inside a drum pad in Bitwig Studio's drum machine, and this pad is choked by | |||||
// another one | |||||
// - the user double clicks the DAW's stop button in the transport which then stops the sound on | |||||
// every tracks | |||||
// | |||||
// NOTE_END is sent by the plugin to the host. The port, channel, key and note_id are those given | |||||
// by the host in the NOTE_ON event. In other words, this event is matched against the | |||||
// plugin's note input port. | |||||
// NOTE_END is useful to help the host to match the plugin's voice life time. | |||||
// | |||||
// When using polyphonic modulations, the host has to allocate and release voices for its | |||||
// polyphonic modulator. Yet only the plugin effectively knows when the host should terminate | |||||
// a voice. NOTE_END solves that issue in a non-intrusive and cooperative way. | |||||
// | |||||
// CLAP assumes that the host will allocate a unique voice on NOTE_ON event for a given port, | |||||
// channel and key. This voice will run until the plugin will instruct the host to terminate | |||||
// it by sending a NOTE_END event. | |||||
// | |||||
// Consider the following sequence: | |||||
// - process() | |||||
// Host->Plugin NoteOn(port:0, channel:0, key:16, time:t0) | |||||
// Host->Plugin NoteOn(port:0, channel:0, key:64, time:t0) | |||||
// Host->Plugin NoteOff(port:0, channel:0, key:16, t1) | |||||
// Host->Plugin NoteOff(port:0, channel:0, key:64, t1) | |||||
// # on t2, both notes did terminate | |||||
// Host->Plugin NoteOn(port:0, channel:0, key:64, t3) | |||||
// # Here the plugin finished processing all the frames and will tell the host | |||||
// # to terminate the voice on key 16 but not 64, because a note has been started at t3 | |||||
// Plugin->Host NoteEnd(port:0, channel:0, key:16, time:ignored) | |||||
// | |||||
// These four events use clap_event_note. | |||||
CLAP_EVENT_NOTE_ON, | |||||
CLAP_EVENT_NOTE_OFF, | |||||
CLAP_EVENT_NOTE_CHOKE, | |||||
CLAP_EVENT_NOTE_END, | |||||
// Represents a note expression. | |||||
// Uses clap_event_note_expression. | |||||
CLAP_EVENT_NOTE_EXPRESSION, | |||||
// PARAM_VALUE sets the parameter's value; uses clap_event_param_value. | |||||
// PARAM_MOD sets the parameter's modulation amount; uses clap_event_param_mod. | |||||
// | |||||
// The value heard is: param_value + param_mod. | |||||
// | |||||
// In case of a concurrent global value/modulation versus a polyphonic one, | |||||
// the voice should only use the polyphonic one and the polyphonic modulation | |||||
// amount will already include the monophonic signal. | |||||
CLAP_EVENT_PARAM_VALUE, | |||||
CLAP_EVENT_PARAM_MOD, | |||||
// Indicates that the user started or finished adjusting a knob. | |||||
// This is not mandatory to wrap parameter changes with gesture events, but this improves | |||||
// the user experience a lot when recording automation or overriding automation playback. | |||||
// Uses clap_event_param_gesture. | |||||
CLAP_EVENT_PARAM_GESTURE_BEGIN, | |||||
CLAP_EVENT_PARAM_GESTURE_END, | |||||
CLAP_EVENT_TRANSPORT, // update the transport info; clap_event_transport | |||||
CLAP_EVENT_MIDI, // raw midi event; clap_event_midi | |||||
CLAP_EVENT_MIDI_SYSEX, // raw midi sysex event; clap_event_midi_sysex | |||||
CLAP_EVENT_MIDI2, // raw midi 2 event; clap_event_midi2 | |||||
}; | |||||
// Note on, off, end and choke events. | |||||
// In the case of note choke or end events: | |||||
// - the velocity is ignored. | |||||
// - key and channel are used to match active notes, a value of -1 matches all. | |||||
typedef struct clap_event_note { | |||||
clap_event_header_t header; | |||||
int32_t note_id; // -1 if unspecified, otherwise >=0 | |||||
int16_t port_index; | |||||
int16_t channel; // 0..15 | |||||
int16_t key; // 0..127 | |||||
double velocity; // 0..1 | |||||
} clap_event_note_t; | |||||
enum { | |||||
// with 0 < x <= 4, plain = 20 * log(x) | |||||
CLAP_NOTE_EXPRESSION_VOLUME, | |||||
// pan, 0 left, 0.5 center, 1 right | |||||
CLAP_NOTE_EXPRESSION_PAN, | |||||
// relative tuning in semitone, from -120 to +120 | |||||
CLAP_NOTE_EXPRESSION_TUNING, | |||||
// 0..1 | |||||
CLAP_NOTE_EXPRESSION_VIBRATO, | |||||
CLAP_NOTE_EXPRESSION_EXPRESSION, | |||||
CLAP_NOTE_EXPRESSION_BRIGHTNESS, | |||||
CLAP_NOTE_EXPRESSION_PRESSURE, | |||||
}; | |||||
typedef int32_t clap_note_expression; | |||||
typedef struct clap_event_note_expression { | |||||
clap_event_header_t header; | |||||
clap_note_expression expression_id; | |||||
// target a specific note_id, port, key and channel, -1 for global | |||||
int32_t note_id; | |||||
int16_t port_index; | |||||
int16_t channel; | |||||
int16_t key; | |||||
double value; // see expression for the range | |||||
} clap_event_note_expression_t; | |||||
typedef struct clap_event_param_value { | |||||
clap_event_header_t header; | |||||
// target parameter | |||||
clap_id param_id; // @ref clap_param_info.id | |||||
void *cookie; // @ref clap_param_info.cookie | |||||
// target a specific note_id, port, key and channel, -1 for global | |||||
int32_t note_id; | |||||
int16_t port_index; | |||||
int16_t channel; | |||||
int16_t key; | |||||
double value; | |||||
} clap_event_param_value_t; | |||||
typedef struct clap_event_param_mod { | |||||
clap_event_header_t header; | |||||
// target parameter | |||||
clap_id param_id; // @ref clap_param_info.id | |||||
void *cookie; // @ref clap_param_info.cookie | |||||
// target a specific note_id, port, key and channel, -1 for global | |||||
int32_t note_id; | |||||
int16_t port_index; | |||||
int16_t channel; | |||||
int16_t key; | |||||
double amount; // modulation amount | |||||
} clap_event_param_mod_t; | |||||
typedef struct clap_event_param_gesture { | |||||
clap_event_header_t header; | |||||
// target parameter | |||||
clap_id param_id; // @ref clap_param_info.id | |||||
} clap_event_param_gesture_t; | |||||
enum clap_transport_flags { | |||||
CLAP_TRANSPORT_HAS_TEMPO = 1 << 0, | |||||
CLAP_TRANSPORT_HAS_BEATS_TIMELINE = 1 << 1, | |||||
CLAP_TRANSPORT_HAS_SECONDS_TIMELINE = 1 << 2, | |||||
CLAP_TRANSPORT_HAS_TIME_SIGNATURE = 1 << 3, | |||||
CLAP_TRANSPORT_IS_PLAYING = 1 << 4, | |||||
CLAP_TRANSPORT_IS_RECORDING = 1 << 5, | |||||
CLAP_TRANSPORT_IS_LOOP_ACTIVE = 1 << 6, | |||||
CLAP_TRANSPORT_IS_WITHIN_PRE_ROLL = 1 << 7, | |||||
}; | |||||
typedef struct clap_event_transport { | |||||
clap_event_header_t header; | |||||
uint32_t flags; // see clap_transport_flags | |||||
clap_beattime song_pos_beats; // position in beats | |||||
clap_sectime song_pos_seconds; // position in seconds | |||||
double tempo; // in bpm | |||||
double tempo_inc; // tempo increment for each samples and until the next | |||||
// time info event | |||||
clap_beattime loop_start_beats; | |||||
clap_beattime loop_end_beats; | |||||
clap_sectime loop_start_seconds; | |||||
clap_sectime loop_end_seconds; | |||||
clap_beattime bar_start; // start pos of the current bar | |||||
int32_t bar_number; // bar at song pos 0 has the number 0 | |||||
uint16_t tsig_num; // time signature numerator | |||||
uint16_t tsig_denom; // time signature denominator | |||||
} clap_event_transport_t; | |||||
typedef struct clap_event_midi { | |||||
clap_event_header_t header; | |||||
uint16_t port_index; | |||||
uint8_t data[3]; | |||||
} clap_event_midi_t; | |||||
typedef struct clap_event_midi_sysex { | |||||
clap_event_header_t header; | |||||
uint16_t port_index; | |||||
const uint8_t *buffer; // midi buffer | |||||
uint32_t size; | |||||
} clap_event_midi_sysex_t; | |||||
// While it is possible to use a series of midi2 event to send a sysex, | |||||
// prefer clap_event_midi_sysex if possible for efficiency. | |||||
typedef struct clap_event_midi2 { | |||||
clap_event_header_t header; | |||||
uint16_t port_index; | |||||
uint32_t data[4]; | |||||
} clap_event_midi2_t; | |||||
// Input event list, events must be sorted by time. | |||||
typedef struct clap_input_events { | |||||
void *ctx; // reserved pointer for the list | |||||
uint32_t (*size)(const struct clap_input_events *list); | |||||
// Don't free the returned event, it belongs to the list | |||||
const clap_event_header_t *(*get)(const struct clap_input_events *list, uint32_t index); | |||||
} clap_input_events_t; | |||||
// Output event list, events must be sorted by time. | |||||
typedef struct clap_output_events { | |||||
void *ctx; // reserved pointer for the list | |||||
// Pushes a copy of the event | |||||
// returns false if the event could not be pushed to the queue (out of memory?) | |||||
bool (*try_push)(const struct clap_output_events *list, const clap_event_header_t *event); | |||||
} clap_output_events_t; | |||||
#ifdef __cplusplus | |||||
} | |||||
#endif |
@@ -0,0 +1,116 @@ | |||||
#pragma once | |||||
#include "../plugin.h" | |||||
#include "../string-sizes.h" | |||||
/// @page Audio Ports | |||||
/// | |||||
/// This extension provides a way for the plugin to describe its current audio ports. | |||||
/// | |||||
/// If the plugin does not implement this extension, it won't have audio ports. | |||||
/// | |||||
/// 32 bits support is required for both host and plugins. 64 bits audio is optional. | |||||
/// | |||||
/// The plugin is only allowed to change its ports configuration while it is deactivated. | |||||
static CLAP_CONSTEXPR const char CLAP_EXT_AUDIO_PORTS[] = "clap.audio-ports"; | |||||
static CLAP_CONSTEXPR const char CLAP_PORT_MONO[] = "mono"; | |||||
static CLAP_CONSTEXPR const char CLAP_PORT_STEREO[] = "stereo"; | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#endif | |||||
enum { | |||||
// This port is the main audio input or output. | |||||
// There can be only one main input and main output. | |||||
// Main port must be at index 0. | |||||
CLAP_AUDIO_PORT_IS_MAIN = 1 << 0, | |||||
// This port can be used with 64 bits audio | |||||
CLAP_AUDIO_PORT_SUPPORTS_64BITS = 1 << 1, | |||||
// 64 bits audio is preferred with this port | |||||
CLAP_AUDIO_PORT_PREFERS_64BITS = 1 << 2, | |||||
// This port must be used with the same sample size as all the other ports which have this flag. | |||||
// In other words if all ports have this flag then the plugin may either be used entirely with | |||||
// 64 bits audio or 32 bits audio, but it can't be mixed. | |||||
CLAP_AUDIO_PORT_REQUIRES_COMMON_SAMPLE_SIZE = 1 << 3, | |||||
}; | |||||
typedef struct clap_audio_port_info { | |||||
// id identifies a port and must be stable. | |||||
// id may overlap between input and output ports. | |||||
clap_id id; | |||||
char name[CLAP_NAME_SIZE]; // displayable name | |||||
uint32_t flags; | |||||
uint32_t channel_count; | |||||
// If null or empty then it is unspecified (arbitrary audio). | |||||
// This filed can be compared against: | |||||
// - CLAP_PORT_MONO | |||||
// - CLAP_PORT_STEREO | |||||
// - CLAP_PORT_SURROUND (defined in the surround extension) | |||||
// - CLAP_PORT_AMBISONIC (defined in the ambisonic extension) | |||||
// - CLAP_PORT_CV (defined in the cv extension) | |||||
// | |||||
// An extension can provide its own port type and way to inspect the channels. | |||||
const char *port_type; | |||||
// in-place processing: allow the host to use the same buffer for input and output | |||||
// if supported set the pair port id. | |||||
// if not supported set to CLAP_INVALID_ID | |||||
clap_id in_place_pair; | |||||
} clap_audio_port_info_t; | |||||
// The audio ports scan has to be done while the plugin is deactivated. | |||||
typedef struct clap_plugin_audio_ports { | |||||
// number of ports, for either input or output | |||||
// [main-thread] | |||||
uint32_t (*count)(const clap_plugin_t *plugin, bool is_input); | |||||
// get info about about an audio port. | |||||
// [main-thread] | |||||
bool (*get)(const clap_plugin_t *plugin, | |||||
uint32_t index, | |||||
bool is_input, | |||||
clap_audio_port_info_t *info); | |||||
} clap_plugin_audio_ports_t; | |||||
enum { | |||||
// The ports name did change, the host can scan them right away. | |||||
CLAP_AUDIO_PORTS_RESCAN_NAMES = 1 << 0, | |||||
// [!active] The flags did change | |||||
CLAP_AUDIO_PORTS_RESCAN_FLAGS = 1 << 1, | |||||
// [!active] The channel_count did change | |||||
CLAP_AUDIO_PORTS_RESCAN_CHANNEL_COUNT = 1 << 2, | |||||
// [!active] The port type did change | |||||
CLAP_AUDIO_PORTS_RESCAN_PORT_TYPE = 1 << 3, | |||||
// [!active] The in-place pair did change, this requires. | |||||
CLAP_AUDIO_PORTS_RESCAN_IN_PLACE_PAIR = 1 << 4, | |||||
// [!active] The list of ports have changed: entries have been removed/added. | |||||
CLAP_AUDIO_PORTS_RESCAN_LIST = 1 << 5, | |||||
}; | |||||
typedef struct clap_host_audio_ports { | |||||
// Checks if the host allows a plugin to change a given aspect of the audio ports definition. | |||||
// [main-thread] | |||||
bool (*is_rescan_flag_supported)(const clap_host_t *host, uint32_t flag); | |||||
// Rescan the full list of audio ports according to the flags. | |||||
// It is illegal to ask the host to rescan with a flag that is not supported. | |||||
// Certain flags require the plugin to be de-activated. | |||||
// [main-thread] | |||||
void (*rescan)(const clap_host_t *host, uint32_t flags); | |||||
} clap_host_audio_ports_t; | |||||
#ifdef __cplusplus | |||||
} | |||||
#endif |
@@ -0,0 +1,219 @@ | |||||
#pragma once | |||||
#include "../plugin.h" | |||||
/// @page GUI | |||||
/// | |||||
/// This extension defines how the plugin will present its GUI. | |||||
/// | |||||
/// There are two approaches: | |||||
/// 1. the plugin creates a window and embeds it into the host's window | |||||
/// 2. the plugin creates a floating window | |||||
/// | |||||
/// Embedding the window gives more control to the host, and feels more integrated. | |||||
/// Floating window are sometimes the only option due to technical limitations. | |||||
/// | |||||
/// Showing the GUI works as follow: | |||||
/// 1. clap_plugin_gui->is_api_supported(), check what can work | |||||
/// 2. clap_plugin_gui->create(), allocates gui resources | |||||
/// 3. if the plugin window is floating | |||||
/// 4. -> clap_plugin_gui->set_transient() | |||||
/// 5. -> clap_plugin_gui->suggest_title() | |||||
/// 6. else | |||||
/// 7. -> clap_plugin_gui->set_scale() | |||||
/// 8. -> clap_plugin_gui->can_resize() | |||||
/// 9. -> if resizable and has known size from previous session, clap_plugin_gui->set_size() | |||||
/// 10. -> else clap_plugin_gui->get_size(), gets initial size | |||||
/// 11. -> clap_plugin_gui->set_parent() | |||||
/// 12. clap_plugin_gui->show() | |||||
/// 13. clap_plugin_gui->hide()/show() ... | |||||
/// 14. clap_plugin_gui->destroy() when done with the gui | |||||
/// | |||||
/// Resizing the window (initiated by the plugin, if embedded): | |||||
/// 1. Plugins calls clap_host_gui->request_resize() | |||||
/// 2. If the host returns true the new size is accepted, | |||||
/// the host doesn't have to call clap_plugin_gui->set_size(). | |||||
/// If the host returns false, the new size is rejected. | |||||
/// | |||||
/// Resizing the window (drag, if embedded)): | |||||
/// 1. Only possible if clap_plugin_gui->can_resize() returns true | |||||
/// 2. Mouse drag -> new_size | |||||
/// 3. clap_plugin_gui->adjust_size(new_size) -> working_size | |||||
/// 4. clap_plugin_gui->set_size(working_size) | |||||
static CLAP_CONSTEXPR const char CLAP_EXT_GUI[] = "clap.gui"; | |||||
// If your windowing API is not listed here, please open an issue and we'll figure it out. | |||||
// https://github.com/free-audio/clap/issues/new | |||||
// uses physical size | |||||
// embed using https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setparent | |||||
static const CLAP_CONSTEXPR char CLAP_WINDOW_API_WIN32[] = "win32"; | |||||
// uses logical size, don't call clap_plugin_gui->set_scale() | |||||
static const CLAP_CONSTEXPR char CLAP_WINDOW_API_COCOA[] = "cocoa"; | |||||
// uses physical size | |||||
// embed using https://specifications.freedesktop.org/xembed-spec/xembed-spec-latest.html | |||||
static const CLAP_CONSTEXPR char CLAP_WINDOW_API_X11[] = "x11"; | |||||
// uses physical size | |||||
// embed is currently not supported, use floating windows | |||||
static const CLAP_CONSTEXPR char CLAP_WINDOW_API_WAYLAND[] = "wayland"; | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#endif | |||||
typedef void *clap_hwnd; | |||||
typedef void *clap_nsview; | |||||
typedef unsigned long clap_xwnd; | |||||
// Represent a window reference. | |||||
typedef struct clap_window { | |||||
const char *api; // one of CLAP_WINDOW_API_XXX | |||||
union { | |||||
clap_nsview cocoa; | |||||
clap_xwnd x11; | |||||
clap_hwnd win32; | |||||
void *ptr; // for anything defined outside of clap | |||||
uintptr_t uptr; | |||||
}; | |||||
} clap_window_t; | |||||
// Information to improve window resizing when initiated by the host or window manager. | |||||
typedef struct clap_gui_resize_hints { | |||||
bool can_resize_horizontally; | |||||
bool can_resize_vertically; | |||||
// only if can resize horizontally and vertically | |||||
bool preserve_aspect_ratio; | |||||
uint32_t aspect_ratio_width; | |||||
uint32_t aspect_ratio_height; | |||||
} clap_gui_resize_hints_t; | |||||
// Size (width, height) is in pixels; the corresponding windowing system extension is | |||||
// responsible for defining if it is physical pixels or logical pixels. | |||||
typedef struct clap_plugin_gui { | |||||
// Returns true if the requested gui api is supported | |||||
// [main-thread] | |||||
bool (*is_api_supported)(const clap_plugin_t *plugin, const char *api, bool is_floating); | |||||
// Returns true if the plugin has a preferred api. | |||||
// The host has no obligation to honor the plugin preferrence, this is just a hint. | |||||
// [main-thread] | |||||
bool (*get_preferred_api)(const clap_plugin_t *plugin, const char **api, bool *is_floating); | |||||
// Create and allocate all resources necessary for the gui. | |||||
// | |||||
// If is_floating is true, then the window will not be managed by the host. The plugin | |||||
// can set its window to stays above the parent window, see set_transient(). | |||||
// api may be null or blank for floating window. | |||||
// | |||||
// If is_floating is false, then the plugin has to embbed its window into the parent window, see | |||||
// set_parent(). | |||||
// | |||||
// After this call, the GUI may not be visible yet; don't forget to call show(). | |||||
// [main-thread] | |||||
bool (*create)(const clap_plugin_t *plugin, const char *api, bool is_floating); | |||||
// Free all resources associated with the gui. | |||||
// [main-thread] | |||||
void (*destroy)(const clap_plugin_t *plugin); | |||||
// Set the absolute GUI scaling factor, and override any OS info. | |||||
// Should not be used if the windowing api relies upon logical pixels. | |||||
// | |||||
// If the plugin prefers to work out the scaling factor itself by querying the OS directly, | |||||
// then ignore the call. | |||||
// | |||||
// Returns true if the scaling could be applied | |||||
// Returns false if the call was ignored, or the scaling could not be applied. | |||||
// [main-thread] | |||||
bool (*set_scale)(const clap_plugin_t *plugin, double scale); | |||||
// Get the current size of the plugin UI. | |||||
// clap_plugin_gui->create() must have been called prior to asking the size. | |||||
// [main-thread] | |||||
bool (*get_size)(const clap_plugin_t *plugin, uint32_t *width, uint32_t *height); | |||||
// Returns true if the window is resizeable (mouse drag). | |||||
// Only for embedded windows. | |||||
// [main-thread] | |||||
bool (*can_resize)(const clap_plugin_t *plugin); | |||||
// Returns true if the plugin can provide hints on how to resize the window. | |||||
// [main-thread] | |||||
bool (*get_resize_hints)(const clap_plugin_t *plugin, clap_gui_resize_hints_t *hints); | |||||
// If the plugin gui is resizable, then the plugin will calculate the closest | |||||
// usable size which fits in the given size. | |||||
// This method does not change the size. | |||||
// | |||||
// Only for embedded windows. | |||||
// [main-thread] | |||||
bool (*adjust_size)(const clap_plugin_t *plugin, uint32_t *width, uint32_t *height); | |||||
// Sets the window size. Only for embedded windows. | |||||
// [main-thread] | |||||
bool (*set_size)(const clap_plugin_t *plugin, uint32_t width, uint32_t height); | |||||
// Embbeds the plugin window into the given window. | |||||
// [main-thread & !floating] | |||||
bool (*set_parent)(const clap_plugin_t *plugin, const clap_window_t *window); | |||||
// Set the plugin floating window to stay above the given window. | |||||
// [main-thread & floating] | |||||
bool (*set_transient)(const clap_plugin_t *plugin, const clap_window_t *window); | |||||
// Suggests a window title. Only for floating windows. | |||||
// [main-thread & floating] | |||||
void (*suggest_title)(const clap_plugin_t *plugin, const char *title); | |||||
// Show the window. | |||||
// [main-thread] | |||||
bool (*show)(const clap_plugin_t *plugin); | |||||
// Hide the window, this method does not free the resources, it just hides | |||||
// the window content. Yet it may be a good idea to stop painting timers. | |||||
// [main-thread] | |||||
bool (*hide)(const clap_plugin_t *plugin); | |||||
} clap_plugin_gui_t; | |||||
typedef struct clap_host_gui { | |||||
// The host should call get_resize_hints() again. | |||||
// [thread-safe] | |||||
void (*resize_hints_changed)(const clap_host_t *host); | |||||
/* Request the host to resize the client area to width, height. | |||||
* Return true if the new size is accepted, false otherwise. | |||||
* The host doesn't have to call set_size(). | |||||
* | |||||
* Note: if not called from the main thread, then a return value simply means that the host | |||||
* acknowledged the request and will process it asynchronously. If the request then can't be | |||||
* satisfied then the host will call set_size() to revert the operation. | |||||
* | |||||
* [thread-safe] */ | |||||
bool (*request_resize)(const clap_host_t *host, uint32_t width, uint32_t height); | |||||
/* Request the host to show the plugin gui. | |||||
* Return true on success, false otherwise. | |||||
* [thread-safe] */ | |||||
bool (*request_show)(const clap_host_t *host); | |||||
/* Request the host to hide the plugin gui. | |||||
* Return true on success, false otherwise. | |||||
* [thread-safe] */ | |||||
bool (*request_hide)(const clap_host_t *host); | |||||
// The floating window has been closed, or the connection to the gui has been lost. | |||||
// | |||||
// If was_destroyed is true, then the host must call clap_plugin_gui->destroy() to acknowledge | |||||
// the gui destruction. | |||||
// [thread-safe] | |||||
void (*closed)(const clap_host_t *host, bool was_destroyed); | |||||
} clap_host_gui_t; | |||||
#ifdef __cplusplus | |||||
} | |||||
#endif |
@@ -0,0 +1,78 @@ | |||||
#pragma once | |||||
#include "../plugin.h" | |||||
#include "../string-sizes.h" | |||||
/// @page Note Ports | |||||
/// | |||||
/// This extension provides a way for the plugin to describe its current note ports. | |||||
/// If the plugin does not implement this extension, it won't have note input or output. | |||||
/// The plugin is only allowed to change its note ports configuration while it is deactivated. | |||||
static CLAP_CONSTEXPR const char CLAP_EXT_NOTE_PORTS[] = "clap.note-ports"; | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#endif | |||||
enum clap_note_dialect { | |||||
// Uses clap_event_note and clap_event_note_expression. | |||||
CLAP_NOTE_DIALECT_CLAP = 1 << 0, | |||||
// Uses clap_event_midi, no polyphonic expression | |||||
CLAP_NOTE_DIALECT_MIDI = 1 << 1, | |||||
// Uses clap_event_midi, with polyphonic expression (MPE) | |||||
CLAP_NOTE_DIALECT_MIDI_MPE = 1 << 2, | |||||
// Uses clap_event_midi2 | |||||
CLAP_NOTE_DIALECT_MIDI2 = 1 << 3, | |||||
}; | |||||
typedef struct clap_note_port_info { | |||||
// id identifies a port and must be stable. | |||||
// id may overlap between input and output ports. | |||||
clap_id id; | |||||
uint32_t supported_dialects; // bitfield, see clap_note_dialect | |||||
uint32_t preferred_dialect; // one value of clap_note_dialect | |||||
char name[CLAP_NAME_SIZE]; // displayable name, i18n? | |||||
} clap_note_port_info_t; | |||||
// The note ports scan has to be done while the plugin is deactivated. | |||||
typedef struct clap_plugin_note_ports { | |||||
// number of ports, for either input or output | |||||
// [main-thread] | |||||
uint32_t (*count)(const clap_plugin_t *plugin, bool is_input); | |||||
// get info about about a note port. | |||||
// [main-thread] | |||||
bool (*get)(const clap_plugin_t *plugin, | |||||
uint32_t index, | |||||
bool is_input, | |||||
clap_note_port_info_t *info); | |||||
} clap_plugin_note_ports_t; | |||||
enum { | |||||
// The ports have changed, the host shall perform a full scan of the ports. | |||||
// This flag can only be used if the plugin is not active. | |||||
// If the plugin active, call host->request_restart() and then call rescan() | |||||
// when the host calls deactivate() | |||||
CLAP_NOTE_PORTS_RESCAN_ALL = 1 << 0, | |||||
// The ports name did change, the host can scan them right away. | |||||
CLAP_NOTE_PORTS_RESCAN_NAMES = 1 << 1, | |||||
}; | |||||
typedef struct clap_host_note_ports { | |||||
// Query which dialects the host supports | |||||
// [main-thread] | |||||
uint32_t (*supported_dialects)(const clap_host_t *host); | |||||
// Rescan the full list of note ports according to the flags. | |||||
// [main-thread] | |||||
void (*rescan)(const clap_host_t *host, uint32_t flags); | |||||
} clap_host_note_ports_t; | |||||
#ifdef __cplusplus | |||||
} | |||||
#endif |
@@ -0,0 +1,296 @@ | |||||
#pragma once | |||||
#include "../plugin.h" | |||||
#include "../string-sizes.h" | |||||
/// @page Parameters | |||||
/// @brief parameters management | |||||
/// | |||||
/// Main idea: | |||||
/// | |||||
/// The host sees the plugin as an atomic entity; and acts as a controller on top of its parameters. | |||||
/// The plugin is responsible for keeping its audio processor and its GUI in sync. | |||||
/// | |||||
/// The host can at any time read parameters' value on the [main-thread] using | |||||
/// @ref clap_plugin_params.value(). | |||||
/// | |||||
/// There are two options to communicate parameter value changes, and they are not concurrent. | |||||
/// - send automation points during clap_plugin.process() | |||||
/// - send automation points during clap_plugin_params.flush(), for parameter changes | |||||
/// without processing audio | |||||
/// | |||||
/// When the plugin changes a parameter value, it must inform the host. | |||||
/// It will send @ref CLAP_EVENT_PARAM_VALUE event during process() or flush(). | |||||
/// If the user is adjusting the value, don't forget to mark the begining and end | |||||
/// of the gesture by sending CLAP_EVENT_PARAM_GESTURE_BEGIN and CLAP_EVENT_PARAM_GESTURE_END | |||||
/// events. | |||||
/// | |||||
/// @note MIDI CCs are tricky because you may not know when the parameter adjustment ends. | |||||
/// Also if the host records incoming MIDI CC and parameter change automation at the same time, | |||||
/// there will be a conflict at playback: MIDI CC vs Automation. | |||||
/// The parameter automation will always target the same parameter because the param_id is stable. | |||||
/// The MIDI CC may have a different mapping in the future and may result in a different playback. | |||||
/// | |||||
/// When a MIDI CC changes a parameter's value, set the flag CLAP_EVENT_DONT_RECORD in | |||||
/// clap_event_param.header.flags. That way the host may record the MIDI CC automation, but not the | |||||
/// parameter change and there won't be conflict at playback. | |||||
/// | |||||
/// Scenarios: | |||||
/// | |||||
/// I. Loading a preset | |||||
/// - load the preset in a temporary state | |||||
/// - call @ref clap_host_params.rescan() if anything changed | |||||
/// - call @ref clap_host_latency.changed() if latency changed | |||||
/// - invalidate any other info that may be cached by the host | |||||
/// - if the plugin is activated and the preset will introduce breaking changes | |||||
/// (latency, audio ports, new parameters, ...) be sure to wait for the host | |||||
/// to deactivate the plugin to apply those changes. | |||||
/// If there are no breaking changes, the plugin can apply them them right away. | |||||
/// The plugin is resonsible for updating both its audio processor and its gui. | |||||
/// | |||||
/// II. Turning a knob on the DAW interface | |||||
/// - the host will send an automation event to the plugin via a process() or flush() | |||||
/// | |||||
/// III. Turning a knob on the Plugin interface | |||||
/// - the plugin is responsible for sending the parameter value to its audio processor | |||||
/// - call clap_host_params->request_flush() or clap_host->request_process(). | |||||
/// - when the host calls either clap_plugin->process() or clap_plugin_params->flush(), | |||||
/// send an automation event and don't forget to set begin_adjust, | |||||
/// end_adjust and should_record flags | |||||
/// | |||||
/// IV. Turning a knob via automation | |||||
/// - host sends an automation point during clap_plugin->process() or clap_plugin_params->flush(). | |||||
/// - the plugin is responsible for updating its GUI | |||||
/// | |||||
/// V. Turning a knob via plugin's internal MIDI mapping | |||||
/// - the plugin sends a CLAP_EVENT_PARAM_SET output event, set should_record to false | |||||
/// - the plugin is responsible to update its GUI | |||||
/// | |||||
/// VI. Adding or removing parameters | |||||
/// - if the plugin is activated call clap_host->restart() | |||||
/// - once the plugin isn't active: | |||||
/// - apply the new state | |||||
/// - if a parameter is gone or is created with an id that may have been used before, | |||||
/// call clap_host_params.clear(host, param_id, CLAP_PARAM_CLEAR_ALL) | |||||
/// - call clap_host_params->rescan(CLAP_PARAM_RESCAN_ALL) | |||||
static CLAP_CONSTEXPR const char CLAP_EXT_PARAMS[] = "clap.params"; | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#endif | |||||
enum { | |||||
// Is this param stepped? (integer values only) | |||||
// if so the double value is converted to integer using a cast (equivalent to trunc). | |||||
CLAP_PARAM_IS_STEPPED = 1 << 0, | |||||
// Useful for for periodic parameters like a phase | |||||
CLAP_PARAM_IS_PERIODIC = 1 << 1, | |||||
// The parameter should not be shown to the user, because it is currently not used. | |||||
// It is not necessary to process automation for this parameter. | |||||
CLAP_PARAM_IS_HIDDEN = 1 << 2, | |||||
// The parameter can't be changed by the host. | |||||
CLAP_PARAM_IS_READONLY = 1 << 3, | |||||
// This parameter is used to merge the plugin and host bypass button. | |||||
// It implies that the parameter is stepped. | |||||
// min: 0 -> bypass off | |||||
// max: 1 -> bypass on | |||||
CLAP_PARAM_IS_BYPASS = 1 << 4, | |||||
// When set: | |||||
// - automation can be recorded | |||||
// - automation can be played back | |||||
// | |||||
// The host can send live user changes for this parameter regardless of this flag. | |||||
// | |||||
// If this parameters affect the internal processing structure of the plugin, ie: max delay, fft | |||||
// size, ... and the plugins needs to re-allocate its working buffers, then it should call | |||||
// host->request_restart(), and perform the change once the plugin is re-activated. | |||||
CLAP_PARAM_IS_AUTOMATABLE = 1 << 5, | |||||
// Does this parameter support per note automations? | |||||
CLAP_PARAM_IS_AUTOMATABLE_PER_NOTE_ID = 1 << 6, | |||||
// Does this parameter support per key automations? | |||||
CLAP_PARAM_IS_AUTOMATABLE_PER_KEY = 1 << 7, | |||||
// Does this parameter support per channel automations? | |||||
CLAP_PARAM_IS_AUTOMATABLE_PER_CHANNEL = 1 << 8, | |||||
// Does this parameter support per port automations? | |||||
CLAP_PARAM_IS_AUTOMATABLE_PER_PORT = 1 << 9, | |||||
// Does this parameter support the modulation signal? | |||||
CLAP_PARAM_IS_MODULATABLE = 1 << 10, | |||||
// Does this parameter support per note modulations? | |||||
CLAP_PARAM_IS_MODULATABLE_PER_NOTE_ID = 1 << 11, | |||||
// Does this parameter support per key modulations? | |||||
CLAP_PARAM_IS_MODULATABLE_PER_KEY = 1 << 12, | |||||
// Does this parameter support per channel modulations? | |||||
CLAP_PARAM_IS_MODULATABLE_PER_CHANNEL = 1 << 13, | |||||
// Does this parameter support per port modulations? | |||||
CLAP_PARAM_IS_MODULATABLE_PER_PORT = 1 << 14, | |||||
// Any change to this parameter will affect the plugin output and requires to be done via | |||||
// process() if the plugin is active. | |||||
// | |||||
// A simple example would be a DC Offset, changing it will change the output signal and must be | |||||
// processed. | |||||
CLAP_PARAM_REQUIRES_PROCESS = 1 << 15, | |||||
}; | |||||
typedef uint32_t clap_param_info_flags; | |||||
/* This describes a parameter */ | |||||
typedef struct clap_param_info { | |||||
// stable parameter identifier, it must never change. | |||||
clap_id id; | |||||
clap_param_info_flags flags; | |||||
// This value is optional and set by the plugin. | |||||
// Its purpose is to provide a fast access to the plugin parameter: | |||||
// | |||||
// Parameter *p = findParameter(param_id); | |||||
// param_info->cookie = p; | |||||
// | |||||
// /* and later on */ | |||||
// Parameter *p = (Parameter *)cookie; | |||||
// | |||||
// It is invalidated on clap_host_params->rescan(CLAP_PARAM_RESCAN_ALL) and when the plugin is | |||||
// destroyed. | |||||
void *cookie; | |||||
// the display name | |||||
char name[CLAP_NAME_SIZE]; | |||||
// the module path containing the param, eg:"oscillators/wt1" | |||||
// '/' will be used as a separator to show a tree like structure. | |||||
char module[CLAP_PATH_SIZE]; | |||||
double min_value; // minimum plain value | |||||
double max_value; // maximum plain value | |||||
double default_value; // default plain value | |||||
} clap_param_info_t; | |||||
typedef struct clap_plugin_params { | |||||
// Returns the number of parameters. | |||||
// [main-thread] | |||||
uint32_t (*count)(const clap_plugin_t *plugin); | |||||
// Copies the parameter's info to param_info and returns true on success. | |||||
// [main-thread] | |||||
bool (*get_info)(const clap_plugin_t *plugin, | |||||
uint32_t param_index, | |||||
clap_param_info_t *param_info); | |||||
// Gets the parameter plain value. | |||||
// [main-thread] | |||||
bool (*get_value)(const clap_plugin_t *plugin, clap_id param_id, double *value); | |||||
// Formats the display text for the given parameter value. | |||||
// The host should always format the parameter value to text using this function | |||||
// before displaying it to the user. | |||||
// [main-thread] | |||||
bool (*value_to_text)( | |||||
const clap_plugin_t *plugin, clap_id param_id, double value, char *display, uint32_t size); | |||||
// Converts the display text to a parameter value. | |||||
// [main-thread] | |||||
bool (*text_to_value)(const clap_plugin_t *plugin, | |||||
clap_id param_id, | |||||
const char *display, | |||||
double *value); | |||||
// Flushes a set of parameter changes. | |||||
// This method must not be called concurrently to clap_plugin->process(). | |||||
// | |||||
// [active ? audio-thread : main-thread] | |||||
void (*flush)(const clap_plugin_t *plugin, | |||||
const clap_input_events_t *in, | |||||
const clap_output_events_t *out); | |||||
} clap_plugin_params_t; | |||||
enum { | |||||
// The parameter values did change, eg. after loading a preset. | |||||
// The host will scan all the parameters value. | |||||
// The host will not record those changes as automation points. | |||||
// New values takes effect immediately. | |||||
CLAP_PARAM_RESCAN_VALUES = 1 << 0, | |||||
// The value to text conversion changed, and the text needs to be rendered again. | |||||
CLAP_PARAM_RESCAN_TEXT = 1 << 1, | |||||
// The parameter info did change, use this flag for: | |||||
// - name change | |||||
// - module change | |||||
// - is_periodic (flag) | |||||
// - is_hidden (flag) | |||||
// New info takes effect immediately. | |||||
CLAP_PARAM_RESCAN_INFO = 1 << 2, | |||||
// Invalidates everything the host knows about parameters. | |||||
// It can only be used while the plugin is deactivated. | |||||
// If the plugin is activated use clap_host->restart() and delay any change until the host calls | |||||
// clap_plugin->deactivate(). | |||||
// | |||||
// You must use this flag if: | |||||
// - some parameters were added or removed. | |||||
// - some parameters had critical changes: | |||||
// - is_per_note (flag) | |||||
// - is_per_channel (flag) | |||||
// - is_readonly (flag) | |||||
// - is_bypass (flag) | |||||
// - is_stepped (flag) | |||||
// - is_modulatable (flag) | |||||
// - min_value | |||||
// - max_value | |||||
// - cookie | |||||
CLAP_PARAM_RESCAN_ALL = 1 << 3, | |||||
}; | |||||
typedef uint32_t clap_param_rescan_flags; | |||||
enum { | |||||
// Clears all possible references to a parameter | |||||
CLAP_PARAM_CLEAR_ALL = 1 << 0, | |||||
// Clears all automations to a parameter | |||||
CLAP_PARAM_CLEAR_AUTOMATIONS = 1 << 1, | |||||
// Clears all modulations to a parameter | |||||
CLAP_PARAM_CLEAR_MODULATIONS = 1 << 2, | |||||
}; | |||||
typedef uint32_t clap_param_clear_flags; | |||||
typedef struct clap_host_params { | |||||
// Rescan the full list of parameters according to the flags. | |||||
// [main-thread] | |||||
void (*rescan)(const clap_host_t *host, clap_param_rescan_flags flags); | |||||
// Clears references to a parameter. | |||||
// [main-thread] | |||||
void (*clear)(const clap_host_t *host, clap_id param_id, clap_param_clear_flags flags); | |||||
// Request a parameter flush. | |||||
// | |||||
// The host will then schedule a call to either: | |||||
// - clap_plugin.process() | |||||
// - clap_plugin_params->flush() | |||||
// | |||||
// This function is always safe to use and should not be called from an [audio-thread] as the | |||||
// plugin would already be within process() or flush(). | |||||
// | |||||
// [thread-safe,!audio-thread] | |||||
void (*request_flush)(const clap_host_t *host); | |||||
} clap_host_params_t; | |||||
#ifdef __cplusplus | |||||
} | |||||
#endif |
@@ -0,0 +1,33 @@ | |||||
#pragma once | |||||
#include "../plugin.h" | |||||
#include "../stream.h" | |||||
static CLAP_CONSTEXPR const char CLAP_EXT_STATE[] = "clap.state"; | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#endif | |||||
typedef struct clap_plugin_state { | |||||
// Saves the plugin state into stream. | |||||
// Returns true if the state was correctly saved. | |||||
// [main-thread] | |||||
bool (*save)(const clap_plugin_t *plugin, const clap_ostream_t *stream); | |||||
// Loads the plugin state from stream. | |||||
// Returns true if the state was correctly restored. | |||||
// [main-thread] | |||||
bool (*load)(const clap_plugin_t *plugin, const clap_istream_t *stream); | |||||
} clap_plugin_state_t; | |||||
typedef struct clap_host_state { | |||||
// Tell the host that the plugin state has changed and should be saved again. | |||||
// If a parameter value changes, then it is implicit that the state is dirty. | |||||
// [main-thread] | |||||
void (*mark_dirty)(const clap_host_t *host); | |||||
} clap_host_state_t; | |||||
#ifdef __cplusplus | |||||
} | |||||
#endif |
@@ -0,0 +1,29 @@ | |||||
#pragma once | |||||
#include "../plugin.h" | |||||
static CLAP_CONSTEXPR const char CLAP_EXT_TIMER_SUPPORT[] = "clap.timer-support"; | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#endif | |||||
typedef struct clap_plugin_timer_support { | |||||
// [main-thread] | |||||
void (*on_timer)(const clap_plugin_t *plugin, clap_id timer_id); | |||||
} clap_plugin_timer_support_t; | |||||
typedef struct clap_host_timer_support { | |||||
// Registers a periodic timer. | |||||
// The host may adjust the period if it is under a certain threshold. | |||||
// 30 Hz should be allowed. | |||||
// [main-thread] | |||||
bool (*register_timer)(const clap_host_t *host, uint32_t period_ms, clap_id *timer_id); | |||||
// [main-thread] | |||||
bool (*unregister_timer)(const clap_host_t *host, clap_id timer_id); | |||||
} clap_host_timer_support_t; | |||||
#ifdef __cplusplus | |||||
} | |||||
#endif |
@@ -0,0 +1,16 @@ | |||||
#pragma once | |||||
#include "private/std.h" | |||||
#include "private/macros.h" | |||||
/// We use fixed point representation of beat time and seconds time | |||||
/// Usage: | |||||
/// double x = ...; // in beats | |||||
/// clap_beattime y = round(CLAP_BEATTIME_FACTOR * x); | |||||
// This will never change | |||||
static const CLAP_CONSTEXPR int64_t CLAP_BEATTIME_FACTOR = 1LL << 31; | |||||
static const CLAP_CONSTEXPR int64_t CLAP_SECTIME_FACTOR = 1LL << 31; | |||||
typedef int64_t clap_beattime; | |||||
typedef int64_t clap_sectime; |
@@ -0,0 +1,41 @@ | |||||
#pragma once | |||||
#include "version.h" | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#endif | |||||
typedef struct clap_host { | |||||
clap_version_t clap_version; // initialized to CLAP_VERSION | |||||
void *host_data; // reserved pointer for the host | |||||
// name and version are mandatory. | |||||
const char *name; // eg: "Bitwig Studio" | |||||
const char *vendor; // eg: "Bitwig GmbH" | |||||
const char *url; // eg: "https://bitwig.com" | |||||
const char *version; // eg: "4.3" | |||||
// Query an extension. | |||||
// [thread-safe] | |||||
const void *(*get_extension)(const struct clap_host *host, const char *extension_id); | |||||
// Request the host to deactivate and then reactivate the plugin. | |||||
// The operation may be delayed by the host. | |||||
// [thread-safe] | |||||
void (*request_restart)(const struct clap_host *host); | |||||
// Request the host to activate and start processing the plugin. | |||||
// This is useful if you have external IO and need to wake up the plugin from "sleep". | |||||
// [thread-safe] | |||||
void (*request_process)(const struct clap_host *host); | |||||
// Request the host to schedule a call to plugin->on_main_thread(plugin) on the main thread. | |||||
// [thread-safe] | |||||
void (*request_callback)(const struct clap_host *host); | |||||
} clap_host_t; | |||||
#ifdef __cplusplus | |||||
} | |||||
#endif |
@@ -0,0 +1,8 @@ | |||||
#pragma once | |||||
#include "private/std.h" | |||||
#include "private/macros.h" | |||||
typedef uint32_t clap_id; | |||||
static const CLAP_CONSTEXPR clap_id CLAP_INVALID_ID = UINT32_MAX; |
@@ -0,0 +1,39 @@ | |||||
#pragma once | |||||
#include "plugin.h" | |||||
static const CLAP_CONSTEXPR char CLAP_PLUGIN_FACTORY_ID[] = "clap.plugin-factory"; | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#endif | |||||
// Every method must be thread-safe. | |||||
// It is very important to be able to scan the plugin as quickly as possible. | |||||
// | |||||
// If the content of the factory may change due to external events, like the user installed | |||||
typedef struct clap_plugin_factory { | |||||
// Get the number of plugins available. | |||||
// [thread-safe] | |||||
uint32_t (*get_plugin_count)(const struct clap_plugin_factory *factory); | |||||
// Retrieves a plugin descriptor by its index. | |||||
// Returns null in case of error. | |||||
// The descriptor must not be freed. | |||||
// [thread-safe] | |||||
const clap_plugin_descriptor_t *(*get_plugin_descriptor)( | |||||
const struct clap_plugin_factory *factory, uint32_t index); | |||||
// Create a clap_plugin by its plugin_id. | |||||
// The returned pointer must be freed by calling plugin->destroy(plugin); | |||||
// The plugin is not allowed to use the host callbacks in the create method. | |||||
// Returns null in case of error. | |||||
// [thread-safe] | |||||
const clap_plugin_t *(*create_plugin)(const struct clap_plugin_factory *factory, | |||||
const clap_host_t *host, | |||||
const char *plugin_id); | |||||
} clap_plugin_factory_t; | |||||
#ifdef __cplusplus | |||||
} | |||||
#endif |
@@ -0,0 +1,76 @@ | |||||
#pragma once | |||||
#include "private/macros.h" | |||||
// This file provides a set of standard plugin features meant to be used | |||||
// within clap_plugin_descriptor.features. | |||||
// | |||||
// For practical reasons we'll avoid spaces and use `-` instead to facilitate | |||||
// scripts that generate the feature array. | |||||
// | |||||
// Non-standard features should be formated as follow: "$namespace:$feature" | |||||
///////////////////// | |||||
// Plugin category // | |||||
///////////////////// | |||||
// Add this feature if your plugin can process note events and then produce audio | |||||
#define CLAP_PLUGIN_FEATURE_INSTRUMENT "instrument" | |||||
// Add this feature if your plugin is an audio effect | |||||
#define CLAP_PLUGIN_FEATURE_AUDIO_EFFECT "audio-effect" | |||||
// Add this feature if your plugin is a note effect or a note generator/sequencer | |||||
#define CLAP_PLUGIN_FEATURE_NOTE_EFFECT "note-effect" | |||||
// Add this feature if your plugin is an analyzer | |||||
#define CLAP_PLUGIN_FEATURE_ANALYZER "analyzer" | |||||
///////////////////////// | |||||
// Plugin sub-category // | |||||
///////////////////////// | |||||
#define CLAP_PLUGIN_FEATURE_SYNTHESIZER "synthesizer" | |||||
#define CLAP_PLUGIN_FEATURE_SAMPLER "sampler" | |||||
#define CLAP_PLUGIN_FEATURE_DRUM "drum" // For single drum | |||||
#define CLAP_PLUGIN_FEATURE_DRUM_MACHINE "drum-machine" | |||||
#define CLAP_PLUGIN_FEATURE_FILTER "filter" | |||||
#define CLAP_PLUGIN_FEATURE_PHASER "phaser" | |||||
#define CLAP_PLUGIN_FEATURE_EQUALIZER "equalizer" | |||||
#define CLAP_PLUGIN_FEATURE_DEESSER "de-esser" | |||||
#define CLAP_PLUGIN_FEATURE_PHASE_VOCODER "phase-vocoder" | |||||
#define CLAP_PLUGIN_FEATURE_GRANULAR "granular" | |||||
#define CLAP_PLUGIN_FEATURE_FREQUENCY_SHIFTER "frequency-shifter" | |||||
#define CLAP_PLUGIN_FEATURE_PITCH_SHIFTER "pitch-shifter" | |||||
#define CLAP_PLUGIN_FEATURE_DISTORTION "distortion" | |||||
#define CLAP_PLUGIN_FEATURE_TRANSIENT_SHAPER "transient-shaper" | |||||
#define CLAP_PLUGIN_FEATURE_COMPRESSOR "compressor" | |||||
#define CLAP_PLUGIN_FEATURE_LIMITER "limiter" | |||||
#define CLAP_PLUGIN_FEATURE_FLANGER "flanger" | |||||
#define CLAP_PLUGIN_FEATURE_CHORUS "chorus" | |||||
#define CLAP_PLUGIN_FEATURE_DELAY "delay" | |||||
#define CLAP_PLUGIN_FEATURE_REVERB "reverb" | |||||
#define CLAP_PLUGIN_FEATURE_TREMOLO "tremolo" | |||||
#define CLAP_PLUGIN_FEATURE_GLITCH "glitch" | |||||
#define CLAP_PLUGIN_FEATURE_UTILITY "utility" | |||||
#define CLAP_PLUGIN_FEATURE_PITCH_CORRECTION "pitch-correction" | |||||
#define CLAP_PLUGIN_FEATURE_RESTORATION "restoration" // repair the sound | |||||
#define CLAP_PLUGIN_FEATURE_MULTI_EFFECTS "multi-effects" | |||||
#define CLAP_PLUGIN_FEATURE_MIXING "mixing" | |||||
#define CLAP_PLUGIN_FEATURE_MASTERING "mastering" | |||||
//////////////////////// | |||||
// Audio Capabilities // | |||||
//////////////////////// | |||||
#define CLAP_PLUGIN_FEATURE_MONO "mono" | |||||
#define CLAP_PLUGIN_FEATURE_STEREO "stereo" | |||||
#define CLAP_PLUGIN_FEATURE_SURROUND "surround" | |||||
#define CLAP_PLUGIN_FEATURE_AMBISONIC "ambisonic" |
@@ -0,0 +1,96 @@ | |||||
#pragma once | |||||
#include "private/macros.h" | |||||
#include "host.h" | |||||
#include "process.h" | |||||
#include "plugin-features.h" | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#endif | |||||
typedef struct clap_plugin_descriptor { | |||||
clap_version_t clap_version; // initialized to CLAP_VERSION | |||||
// Mandatory fields must be set and must not be blank. | |||||
// Otherwise the fields can be null or blank, though it is safer to make them blank. | |||||
const char *id; // eg: "com.u-he.diva", mandatory | |||||
const char *name; // eg: "Diva", mandatory | |||||
const char *vendor; // eg: "u-he" | |||||
const char *url; // eg: "https://u-he.com/products/diva/" | |||||
const char *manual_url; // eg: "https://dl.u-he.com/manuals/plugins/diva/Diva-user-guide.pdf" | |||||
const char *support_url; // eg: "https://u-he.com/support/" | |||||
const char *version; // eg: "1.4.4" | |||||
const char *description; // eg: "The spirit of analogue" | |||||
// Arbitrary list of keywords. | |||||
// They can be matched by the host indexer and used to classify the plugin. | |||||
// The array of pointers must be null terminated. | |||||
// For some standard features see plugin-features.h | |||||
const char **features; | |||||
} clap_plugin_descriptor_t; | |||||
typedef struct clap_plugin { | |||||
const clap_plugin_descriptor_t *desc; | |||||
void *plugin_data; // reserved pointer for the plugin | |||||
// Must be called after creating the plugin. | |||||
// If init returns false, the host must destroy the plugin instance. | |||||
// [main-thread] | |||||
bool (*init)(const struct clap_plugin *plugin); | |||||
// Free the plugin and its resources. | |||||
// It is required to deactivate the plugin prior to this call. | |||||
// [main-thread & !active] | |||||
void (*destroy)(const struct clap_plugin *plugin); | |||||
// Activate and deactivate the plugin. | |||||
// In this call the plugin may allocate memory and prepare everything needed for the process | |||||
// call. The process's sample rate will be constant and process's frame count will included in | |||||
// the [min, max] range, which is bounded by [1, INT32_MAX]. | |||||
// Once activated the latency and port configuration must remain constant, until deactivation. | |||||
// | |||||
// [main-thread & !active_state] | |||||
bool (*activate)(const struct clap_plugin *plugin, | |||||
double sample_rate, | |||||
uint32_t min_frames_count, | |||||
uint32_t max_frames_count); | |||||
// [main-thread & active_state] | |||||
void (*deactivate)(const struct clap_plugin *plugin); | |||||
// Call start processing before processing. | |||||
// [audio-thread & active_state & !processing_state] | |||||
bool (*start_processing)(const struct clap_plugin *plugin); | |||||
// Call stop processing before sending the plugin to sleep. | |||||
// [audio-thread & active_state & processing_state] | |||||
void (*stop_processing)(const struct clap_plugin *plugin); | |||||
// - Clears all buffers, performs a full reset of the processing state (filters, oscillators, | |||||
// enveloppes, lfo, ...) and kills all voices. | |||||
// - The parameter's value remain unchanged. | |||||
// - clap_process.steady_time may jump backward. | |||||
// | |||||
// [audio-thread & active_state] | |||||
void (*reset)(const struct clap_plugin *plugin); | |||||
// process audio, events, ... | |||||
// [audio-thread & active_state & processing_state] | |||||
clap_process_status (*process)(const struct clap_plugin *plugin, const clap_process_t *process); | |||||
// Query an extension. | |||||
// The returned pointer is owned by the plugin. | |||||
// [thread-safe] | |||||
const void *(*get_extension)(const struct clap_plugin *plugin, const char *id); | |||||
// Called by the host on the main thread in response to a previous call to: | |||||
// host->request_callback(host); | |||||
// [main-thread] | |||||
void (*on_main_thread)(const struct clap_plugin *plugin); | |||||
} clap_plugin_t; | |||||
#ifdef __cplusplus | |||||
} | |||||
#endif |
@@ -0,0 +1,36 @@ | |||||
#pragma once | |||||
// Define CLAP_EXPORT | |||||
#if !defined(CLAP_EXPORT) | |||||
# if defined _WIN32 || defined __CYGWIN__ | |||||
# ifdef __GNUC__ | |||||
# define CLAP_EXPORT __attribute__((dllexport)) | |||||
# else | |||||
# define CLAP_EXPORT __declspec(dllexport) | |||||
# endif | |||||
# else | |||||
# if __GNUC__ >= 4 || defined(__clang__) | |||||
# define CLAP_EXPORT __attribute__((visibility("default"))) | |||||
# else | |||||
# define CLAP_EXPORT | |||||
# endif | |||||
# endif | |||||
#endif | |||||
#if defined(__cplusplus) && __cplusplus >= 201103L | |||||
# define CLAP_HAS_CXX11 | |||||
# define CLAP_CONSTEXPR constexpr | |||||
#else | |||||
# define CLAP_CONSTEXPR | |||||
#endif | |||||
#if defined(__cplusplus) && __cplusplus >= 201703L | |||||
# define CLAP_HAS_CXX17 | |||||
# define CLAP_NODISCARD [[nodiscard]] | |||||
#else | |||||
# define CLAP_NODISCARD | |||||
#endif | |||||
#if defined(__cplusplus) && __cplusplus >= 202002L | |||||
# define CLAP_HAS_CXX20 | |||||
#endif |
@@ -0,0 +1,16 @@ | |||||
#pragma once | |||||
#include "macros.h" | |||||
#ifdef CLAP_HAS_CXX11 | |||||
# include <cstdint> | |||||
#else | |||||
# include <stdint.h> | |||||
#endif | |||||
#ifdef __cplusplus | |||||
# include <cstddef> | |||||
#else | |||||
# include <stddef.h> | |||||
# include <stdbool.h> | |||||
#endif |
@@ -0,0 +1,65 @@ | |||||
#pragma once | |||||
#include "events.h" | |||||
#include "audio-buffer.h" | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#endif | |||||
enum { | |||||
// Processing failed. The output buffer must be discarded. | |||||
CLAP_PROCESS_ERROR = 0, | |||||
// Processing succeeded, keep processing. | |||||
CLAP_PROCESS_CONTINUE = 1, | |||||
// Processing succeeded, keep processing if the output is not quiet. | |||||
CLAP_PROCESS_CONTINUE_IF_NOT_QUIET = 2, | |||||
// Rely upon the plugin's tail to determine if the plugin should continue to process. | |||||
// see clap_plugin_tail | |||||
CLAP_PROCESS_TAIL = 3, | |||||
// Processing succeeded, but no more processing is required, | |||||
// until the next event or variation in audio input. | |||||
CLAP_PROCESS_SLEEP = 4, | |||||
}; | |||||
typedef int32_t clap_process_status; | |||||
typedef struct clap_process { | |||||
// A steady sample time counter. | |||||
// This field can be used to calculate the sleep duration between two process calls. | |||||
// This value may be specific to this plugin instance and have no relation to what | |||||
// other plugin instances may receive. | |||||
// | |||||
// Set to -1 if not available, otherwise the value must be greater or equal to 0, | |||||
// and must be increased by at least `frames_count` for the next call to process. | |||||
int64_t steady_time; | |||||
// Number of frames to process | |||||
uint32_t frames_count; | |||||
// time info at sample 0 | |||||
// If null, then this is a free running host, no transport events will be provided | |||||
const clap_event_transport_t *transport; | |||||
// Audio buffers, they must have the same count as specified | |||||
// by clap_plugin_audio_ports->get_count(). | |||||
// The index maps to clap_plugin_audio_ports->get_info(). | |||||
const clap_audio_buffer_t *audio_inputs; | |||||
clap_audio_buffer_t *audio_outputs; | |||||
uint32_t audio_inputs_count; | |||||
uint32_t audio_outputs_count; | |||||
// Input and output events. | |||||
// | |||||
// Events must be sorted by time. | |||||
// The input event list can't be modified. | |||||
const clap_input_events_t *in_events; | |||||
const clap_output_events_t *out_events; | |||||
} clap_process_t; | |||||
#ifdef __cplusplus | |||||
} | |||||
#endif |
@@ -0,0 +1,26 @@ | |||||
#pragma once | |||||
#include "private/std.h" | |||||
#include "private/macros.h" | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#endif | |||||
typedef struct clap_istream { | |||||
void *ctx; // reserved pointer for the stream | |||||
// returns the number of bytes read; 0 indicates end of file and -1 a read error | |||||
int64_t (*read)(const struct clap_istream *stream, void *buffer, uint64_t size); | |||||
} clap_istream_t; | |||||
typedef struct clap_ostream { | |||||
void *ctx; // reserved pointer for the stream | |||||
// returns the number of bytes written; -1 on write error | |||||
int64_t (*write)(const struct clap_ostream *stream, const void *buffer, uint64_t size); | |||||
} clap_ostream_t; | |||||
#ifdef __cplusplus | |||||
} | |||||
#endif |
@@ -0,0 +1,21 @@ | |||||
#pragma once | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#endif | |||||
enum { | |||||
// String capacity for names that can be displayed to the user. | |||||
CLAP_NAME_SIZE = 256, | |||||
// String capacity for describing a path, like a parameter in a module hierarchy or path within a | |||||
// set of nested track groups. | |||||
// | |||||
// This is not suited for describing a file path on the disk, as NTFS allows up to 32K long | |||||
// paths. | |||||
CLAP_PATH_SIZE = 1024, | |||||
}; | |||||
#ifdef __cplusplus | |||||
} | |||||
#endif |
@@ -0,0 +1,34 @@ | |||||
#pragma once | |||||
#include "private/macros.h" | |||||
#include "private/std.h" | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#endif | |||||
typedef struct clap_version { | |||||
// This is the major ABI and API design | |||||
// Version 0.X.Y correspond to the development stage, API and ABI are not stable | |||||
// Version 1.X.Y correspont to the release stage, API and ABI are stable | |||||
uint32_t major; | |||||
uint32_t minor; | |||||
uint32_t revision; | |||||
} clap_version_t; | |||||
#ifdef __cplusplus | |||||
} | |||||
#endif | |||||
#define CLAP_VERSION_MAJOR ((uint32_t)1) | |||||
#define CLAP_VERSION_MINOR ((uint32_t)1) | |||||
#define CLAP_VERSION_REVISION ((uint32_t)1) | |||||
#define CLAP_VERSION_INIT {CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION} | |||||
static const CLAP_CONSTEXPR clap_version_t CLAP_VERSION = CLAP_VERSION_INIT; | |||||
CLAP_NODISCARD static inline CLAP_CONSTEXPR bool | |||||
clap_version_is_compatible(const clap_version_t v) { | |||||
// versions 0.x.y were used during development stage and aren't compatible | |||||
return v.major >= 1; | |||||
} |
@@ -0,0 +1,37 @@ | |||||
/* | |||||
* Carla LADSPA utils | |||||
* Copyright (C) 2022 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_CLAP_UTILS_HPP_INCLUDED | |||||
#define CARLA_CLAP_UTILS_HPP_INCLUDED | |||||
#include "CarlaUtils.hpp" | |||||
#include "clap/entry.h" | |||||
#include "clap/plugin-factory.h" | |||||
#include "clap/plugin-features.h" | |||||
#include "clap/ext/audio-ports.h" | |||||
#include "clap/ext/note-ports.h" | |||||
#include "clap/ext/gui.h" | |||||
#include "clap/ext/params.h" | |||||
#include "clap/ext/state.h" | |||||
#include "clap/ext/timer-support.h" | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
#endif // CARLA_CLAP_UTILS_HPP_INCLUDED |
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* Carla library utils | * Carla library utils | ||||
* Copyright (C) 2011-2014 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* This program is free software; you can redistribute it and/or | * This program is free software; you can redistribute it and/or | ||||
* modify it under the terms of the GNU General Public License as | * modify it under the terms of the GNU General Public License as | ||||
@@ -69,7 +69,7 @@ bool lib_close(const lib_t lib) noexcept | |||||
} | } | ||||
/* | /* | ||||
* Get a library symbol (must not be null). | |||||
* Get a library symbol (must not be null) as a function. | |||||
* Returns null if the symbol is not found. | * Returns null if the symbol is not found. | ||||
*/ | */ | ||||
template<typename Func> | template<typename Func> | ||||
@@ -85,12 +85,12 @@ Func lib_symbol(const lib_t lib, const char* const symbol) noexcept | |||||
# pragma GCC diagnostic push | # pragma GCC diagnostic push | ||||
# pragma GCC diagnostic ignored "-Wcast-function-type" | # pragma GCC diagnostic ignored "-Wcast-function-type" | ||||
# endif | # endif | ||||
return (Func)::GetProcAddress(lib, symbol); | |||||
return reinterpret_cast<Func>(::GetProcAddress(lib, symbol)); | |||||
# if defined(__GNUC__) && (__GNUC__ >= 9) | # if defined(__GNUC__) && (__GNUC__ >= 9) | ||||
# pragma GCC diagnostic pop | # pragma GCC diagnostic pop | ||||
# endif | # endif | ||||
#else | #else | ||||
return (Func)(uintptr_t)::dlsym(lib, symbol); | |||||
return reinterpret_cast<Func>(::dlsym(lib, symbol)); | |||||
#endif | #endif | ||||
} CARLA_SAFE_EXCEPTION_RETURN("lib_symbol", nullptr); | } CARLA_SAFE_EXCEPTION_RETURN("lib_symbol", nullptr); | ||||
} | } | ||||