Signed-off-by: falkTX <falktx@falktx.com>pull/1689/head
@@ -28,6 +28,7 @@ | |||
#include "CarlaLv2Utils.hpp" | |||
#include "CarlaVst2Utils.hpp" | |||
#include "CarlaVst3Utils.hpp" | |||
#include "CarlaClapUtils.hpp" | |||
#ifdef CARLA_OS_MAC | |||
# include "CarlaMacUtils.cpp" | |||
@@ -84,17 +85,17 @@ CARLA_BACKEND_USE_NAMESPACE | |||
// ------------------------------------------------------------------------------------------------------------------- | |||
// 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 | |||
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 && | |||
std::strstr(error, "wrong ELF class") == nullptr && | |||
@@ -150,25 +151,25 @@ static void do_cached_check(const PluginType type) | |||
break; | |||
} | |||
# ifdef USING_JUCE | |||
#ifdef USING_JUCE | |||
if (type == PLUGIN_AU) | |||
CarlaJUCE::initialiseJuce_GUI(); | |||
# endif | |||
#endif | |||
const uint count = carla_get_cached_plugin_count(type, plugPath); | |||
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); | |||
print_cached_plugin(pinfo); | |||
} | |||
# ifdef USING_JUCE | |||
#ifdef USING_JUCE | |||
if (type == PLUGIN_AU) | |||
CarlaJUCE::shutdownJuce_GUI(); | |||
# 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 | |||
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 | |||
// ------------------------------------------------------------------------------------------------------------------- | |||
// find all available plugin audio ports | |||
@@ -2200,6 +2480,7 @@ int main(int argc, char* argv[]) | |||
case PLUGIN_LADSPA: | |||
case PLUGIN_DSSI: | |||
case PLUGIN_VST2: | |||
case PLUGIN_CLAP: | |||
openLib = true; | |||
break; | |||
case PLUGIN_VST3: | |||
@@ -2294,6 +2575,7 @@ int main(int argc, char* argv[]) | |||
case PLUGIN_DSSI: | |||
case PLUGIN_VST2: | |||
case PLUGIN_VST3: | |||
case PLUGIN_CLAP: | |||
removeFileFromQuarantine(filename); | |||
break; | |||
default: | |||
@@ -2351,6 +2633,10 @@ int main(int argc, char* argv[]) | |||
break; | |||
#endif | |||
case PLUGIN_CLAP: | |||
do_clap_check(handle, filename, doInit); | |||
break; | |||
case PLUGIN_DLS: | |||
case PLUGIN_GIG: | |||
case PLUGIN_SF2: | |||
@@ -231,6 +231,7 @@ CARLA_KEY_PATHS_DSSI = "Paths/DSSI" | |||
CARLA_KEY_PATHS_LV2 = "Paths/LV2" | |||
CARLA_KEY_PATHS_VST2 = "Paths/VST2" | |||
CARLA_KEY_PATHS_VST3 = "Paths/VST3" | |||
CARLA_KEY_PATHS_CLAP = "Paths/CLAP" | |||
CARLA_KEY_PATHS_SF2 = "Paths/SF2" | |||
CARLA_KEY_PATHS_SFZ = "Paths/SFZ" | |||
CARLA_KEY_PATHS_JSFX = "Paths/JSFX" | |||
@@ -351,6 +352,7 @@ DEFAULT_DSSI_PATH = "" | |||
DEFAULT_LV2_PATH = "" | |||
DEFAULT_VST2_PATH = "" | |||
DEFAULT_VST3_PATH = "" | |||
DEFAULT_CLAP_PATH = "" | |||
DEFAULT_SF2_PATH = "" | |||
DEFAULT_SFZ_PATH = "" | |||
DEFAULT_JSFX_PATH = "" | |||
@@ -399,6 +401,9 @@ if WINDOWS: | |||
DEFAULT_VST3_PATH = COMMONPROGRAMFILES + "\\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_SFZ_PATH = APPDATA + "\\SFZ" | |||
@@ -411,6 +416,7 @@ if WINDOWS: | |||
if COMMONPROGRAMFILESx86: | |||
DEFAULT_VST3_PATH += COMMONPROGRAMFILESx86 + "\\VST3" | |||
DEFAULT_CLAP_PATH += COMMONPROGRAMFILESx86 + "\\CLAP" | |||
elif HAIKU: | |||
splitter = ":" | |||
@@ -432,6 +438,9 @@ elif HAIKU: | |||
DEFAULT_VST3_PATH = HOME + "/.vst3" | |||
DEFAULT_VST3_PATH += ":/system/add-ons/media/vst3plugins" | |||
DEFAULT_CLAP_PATH = HOME + "/.clap" | |||
DEFAULT_CLAP_PATH += ":/system/add-ons/media/clapplugins" | |||
elif MACOS: | |||
splitter = ":" | |||
@@ -450,6 +459,9 @@ elif MACOS: | |||
DEFAULT_VST3_PATH = HOME + "/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 += ":/Applications/REAPER.app/Contents/InstallFiles/Effects" | |||
@@ -482,6 +494,10 @@ else: | |||
DEFAULT_VST3_PATH += ":/usr/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/sf3" | |||
DEFAULT_SF2_PATH += ":/usr/share/sounds/sf2" | |||
@@ -503,10 +519,12 @@ if not WINDOWS: | |||
if os.path.exists(winePrefix): | |||
DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files/VstPlugins" | |||
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)"): | |||
DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files (x86)/VstPlugins" | |||
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 | |||
@@ -539,6 +557,7 @@ if readEnvVars: | |||
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_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_SFZ_PATH = os.getenv("SFZ_PATH", DEFAULT_SFZ_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_VST2_PATH = DEFAULT_VST2_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_SFZ_PATH = DEFAULT_SFZ_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_VST2_PATH | |||
del DEFAULT_VST3_PATH | |||
del DEFAULT_CLAP_PATH | |||
del DEFAULT_SF2_PATH | |||
del DEFAULT_SFZ_PATH | |||
@@ -40,6 +40,7 @@ from carla_backend import ( | |||
PLUGIN_SFZ, | |||
PLUGIN_VST2, | |||
PLUGIN_VST3, | |||
PLUGIN_CLAP, | |||
) | |||
from carla_shared import ( | |||
@@ -80,6 +81,15 @@ def findVST3Binaries(binPath): | |||
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): | |||
bundles = [] | |||
@@ -380,6 +390,9 @@ def checkPluginVST2(filename, tool, wineSettings=None): | |||
def checkPluginVST3(filename, tool, wineSettings=None): | |||
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): | |||
return runCarlaDiscovery(PLUGIN_SF2, "SF2", filename, tool) | |||
@@ -35,6 +35,7 @@ from carla_backend import ( | |||
PLUGIN_LV2, | |||
PLUGIN_SFZ, | |||
PLUGIN_VST2, | |||
PLUGIN_CLAP, | |||
) | |||
from carla_shared import ( | |||
@@ -46,6 +47,7 @@ from carla_shared import ( | |||
CARLA_DEFAULT_SFZ_PATH, | |||
CARLA_DEFAULT_VST2_PATH, | |||
CARLA_DEFAULT_VST3_PATH, | |||
CARLA_DEFAULT_CLAP_PATH, | |||
CARLA_DEFAULT_WINE_AUTO_PREFIX, | |||
CARLA_DEFAULT_WINE_EXECUTABLE, | |||
CARLA_DEFAULT_WINE_FALLBACK_PREFIX, | |||
@@ -57,6 +59,7 @@ from carla_shared import ( | |||
CARLA_KEY_PATHS_SFZ, | |||
CARLA_KEY_PATHS_VST2, | |||
CARLA_KEY_PATHS_VST3, | |||
CARLA_KEY_PATHS_CLAP, | |||
CARLA_KEY_WINE_AUTO_PREFIX, | |||
CARLA_KEY_WINE_EXECUTABLE, | |||
CARLA_KEY_WINE_FALLBACK_PREFIX, | |||
@@ -78,6 +81,7 @@ from .discovery import ( | |||
checkPluginLADSPA, | |||
checkPluginVST2, | |||
checkPluginVST3, | |||
checkPluginCLAP, | |||
findBinaries, | |||
findFilenames, | |||
findMacVSTBundles, | |||
@@ -107,6 +111,7 @@ class SearchPluginsThread(QThread): | |||
self.fCheckLV2 = False | |||
self.fCheckVST2 = False | |||
self.fCheckVST3 = False | |||
self.fCheckCLAP = False | |||
self.fCheckAU = False | |||
self.fCheckSF2 = False | |||
self.fCheckSFZ = False | |||
@@ -152,13 +157,14 @@ class SearchPluginsThread(QThread): | |||
self.fCheckWin32 = win32 | |||
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.fCheckDSSI = dssi | |||
self.fCheckLV2 = lv2 | |||
self.fCheckVST2 = vst2 | |||
self.fCheckVST3 = vst3 and (LINUX or MACOS or WINDOWS) | |||
self.fCheckVST3 = vst3 | |||
self.fCheckCLAP = clap | |||
self.fCheckAU = au and MACOS | |||
self.fCheckSF2 = sf2 | |||
self.fCheckSFZ = sfz | |||
@@ -190,23 +196,18 @@ class SearchPluginsThread(QThread): | |||
pluginCount += 1 | |||
if self.fCheckVST3: | |||
pluginCount += 1 | |||
if self.fCheckCLAP: | |||
pluginCount += 1 | |||
# Increase count by the number of externally discoverable plugin types | |||
if self.fCheckNative: | |||
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: | |||
self.fCurCount += pluginCount | |||
if self.fCheckVST3 and not (LINUX or MACOS): | |||
self.fCurCount -= 1 | |||
if self.fCheckPosix64: | |||
self.fCurCount += pluginCount | |||
if self.fCheckVST3 and not (LINUX or MACOS): | |||
self.fCurCount -= 1 | |||
if self.fCheckWin32: | |||
self.fCurCount += pluginCount | |||
@@ -421,6 +422,43 @@ class SearchPluginsThread(QThread): | |||
if not self.fContinueChecking: | |||
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.fCheckNative: | |||
plugins = self._checkCached(False) | |||
@@ -619,6 +657,42 @@ class SearchPluginsThread(QThread): | |||
self.fLastCheckValue += self.fCurPercentValue | |||
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): | |||
auPlugins = [] | |||
@@ -685,6 +685,13 @@ | |||
</property> | |||
</widget> | |||
</item> | |||
<item> | |||
<widget class="QCheckBox" name="ch_clap"> | |||
<property name="text"> | |||
<string>CLAP</string> | |||
</property> | |||
</widget> | |||
</item> | |||
<item> | |||
<widget class="QCheckBox" name="ch_au"> | |||
<property name="text"> | |||
@@ -941,6 +948,7 @@ | |||
<tabstop>ch_lv2</tabstop> | |||
<tabstop>ch_vst</tabstop> | |||
<tabstop>ch_vst3</tabstop> | |||
<tabstop>ch_clap</tabstop> | |||
<tabstop>ch_au</tabstop> | |||
<tabstop>ch_kits</tabstop> | |||
<tabstop>ch_effects</tabstop> | |||
@@ -166,10 +166,6 @@ class PluginRefreshW(QDialog): | |||
self.ui.ico_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: | |||
self.setWindowModality(Qt.WindowModal) | |||
else: | |||
@@ -187,6 +183,7 @@ class PluginRefreshW(QDialog): | |||
self.ui.ch_dssi.setEnabled(False) | |||
self.ui.ch_vst.setEnabled(False) | |||
self.ui.ch_vst3.setEnabled(False) | |||
self.ui.ch_clap.setEnabled(False) | |||
if not hasLoadedLv2Plugins: | |||
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_vst.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_sf2.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() | |||
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: | |||
check = settings.value("PluginDatabase/SearchAU", True, bool) and self.ui.ch_au.isEnabled() | |||
else: | |||
@@ -346,6 +347,7 @@ class PluginRefreshW(QDialog): | |||
settings.setValue("PluginDatabase/SearchLV2", self.ui.ch_lv2.isChecked()) | |||
settings.setValue("PluginDatabase/SearchVST2", self.ui.ch_vst.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/SearchSF2", self.ui.ch_sf2.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_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.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() | |||
# ----------------------------------------------------------------------------------------------------------------- | |||
@@ -406,9 +408,9 @@ class PluginRefreshW(QDialog): | |||
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_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) | |||
@@ -78,6 +78,13 @@ | |||
</property> | |||
</widget> | |||
</item> | |||
<item> | |||
<widget class="QCheckBox" name="ch_clap"> | |||
<property name="text"> | |||
<string>CLAP</string> | |||
</property> | |||
</widget> | |||
</item> | |||
<item> | |||
<widget class="QCheckBox" name="ch_au"> | |||
<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 | |||
* 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 | |||
* 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. | |||
*/ | |||
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 ignored "-Wcast-function-type" | |||
# endif | |||
return (Func)::GetProcAddress(lib, symbol); | |||
return reinterpret_cast<Func>(::GetProcAddress(lib, symbol)); | |||
# if defined(__GNUC__) && (__GNUC__ >= 9) | |||
# pragma GCC diagnostic pop | |||
# endif | |||
#else | |||
return (Func)(uintptr_t)::dlsym(lib, symbol); | |||
return reinterpret_cast<Func>(::dlsym(lib, symbol)); | |||
#endif | |||
} CARLA_SAFE_EXCEPTION_RETURN("lib_symbol", nullptr); | |||
} | |||