Browse Source

Start of clap plugin discovery

Signed-off-by: falkTX <falktx@falktx.com>
pull/1689/head
falkTX 2 years ago
parent
commit
ec6eaf420e
30 changed files with 2118 additions and 37 deletions
  1. +296
    -10
      source/discovery/carla-discovery.cpp
  2. +21
    -0
      source/frontend/carla_shared.py
  3. +13
    -0
      source/frontend/pluginlist/discovery.py
  4. +84
    -10
      source/frontend/pluginlist/discoverythread.py
  5. +8
    -0
      source/frontend/pluginlist/pluginlistdialog.ui
  6. +15
    -13
      source/frontend/pluginlist/pluginlistrefreshdialog.py
  7. +7
    -0
      source/frontend/pluginlist/pluginlistrefreshdialog.ui
  8. +37
    -0
      source/includes/clap/audio-buffer.h
  9. +68
    -0
      source/includes/clap/entry.h
  10. +283
    -0
      source/includes/clap/events.h
  11. +116
    -0
      source/includes/clap/ext/audio-ports.h
  12. +219
    -0
      source/includes/clap/ext/gui.h
  13. +78
    -0
      source/includes/clap/ext/note-ports.h
  14. +296
    -0
      source/includes/clap/ext/params.h
  15. +33
    -0
      source/includes/clap/ext/state.h
  16. +29
    -0
      source/includes/clap/ext/timer-support.h
  17. +16
    -0
      source/includes/clap/fixedpoint.h
  18. +41
    -0
      source/includes/clap/host.h
  19. +8
    -0
      source/includes/clap/id.h
  20. +39
    -0
      source/includes/clap/plugin-factory.h
  21. +76
    -0
      source/includes/clap/plugin-features.h
  22. +96
    -0
      source/includes/clap/plugin.h
  23. +36
    -0
      source/includes/clap/private/macros.h
  24. +16
    -0
      source/includes/clap/private/std.h
  25. +65
    -0
      source/includes/clap/process.h
  26. +26
    -0
      source/includes/clap/stream.h
  27. +21
    -0
      source/includes/clap/string-sizes.h
  28. +34
    -0
      source/includes/clap/version.h
  29. +37
    -0
      source/utils/CarlaClapUtils.hpp
  30. +4
    -4
      source/utils/CarlaLibUtils.hpp

+ 296
- 10
source/discovery/carla-discovery.cpp View File

@@ -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:


+ 21
- 0
source/frontend/carla_shared.py View File

@@ -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



+ 13
- 0
source/frontend/pluginlist/discovery.py View File

@@ -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)



+ 84
- 10
source/frontend/pluginlist/discoverythread.py View File

@@ -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 = []



+ 8
- 0
source/frontend/pluginlist/pluginlistdialog.ui View File

@@ -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>


+ 15
- 13
source/frontend/pluginlist/pluginlistrefreshdialog.py View File

@@ -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)



+ 7
- 0
source/frontend/pluginlist/pluginlistrefreshdialog.ui View File

@@ -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">


+ 37
- 0
source/includes/clap/audio-buffer.h View File

@@ -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

+ 68
- 0
source/includes/clap/entry.h View File

@@ -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

+ 283
- 0
source/includes/clap/events.h View File

@@ -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

+ 116
- 0
source/includes/clap/ext/audio-ports.h View File

@@ -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

+ 219
- 0
source/includes/clap/ext/gui.h View File

@@ -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

+ 78
- 0
source/includes/clap/ext/note-ports.h View File

@@ -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

+ 296
- 0
source/includes/clap/ext/params.h View File

@@ -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

+ 33
- 0
source/includes/clap/ext/state.h View File

@@ -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

+ 29
- 0
source/includes/clap/ext/timer-support.h View File

@@ -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

+ 16
- 0
source/includes/clap/fixedpoint.h View File

@@ -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;

+ 41
- 0
source/includes/clap/host.h View File

@@ -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

+ 8
- 0
source/includes/clap/id.h View File

@@ -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;

+ 39
- 0
source/includes/clap/plugin-factory.h View File

@@ -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

+ 76
- 0
source/includes/clap/plugin-features.h View File

@@ -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"

+ 96
- 0
source/includes/clap/plugin.h View File

@@ -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

+ 36
- 0
source/includes/clap/private/macros.h View File

@@ -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

+ 16
- 0
source/includes/clap/private/std.h View File

@@ -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

+ 65
- 0
source/includes/clap/process.h View File

@@ -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

+ 26
- 0
source/includes/clap/stream.h View File

@@ -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

+ 21
- 0
source/includes/clap/string-sizes.h View File

@@ -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

+ 34
- 0
source/includes/clap/version.h View File

@@ -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;
}

+ 37
- 0
source/utils/CarlaClapUtils.hpp View File

@@ -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

+ 4
- 4
source/utils/CarlaLibUtils.hpp View File

@@ -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);
}


Loading…
Cancel
Save