|
|
|
@@ -31,6 +31,7 @@ static bool isCachedPluginType(const CB::PluginType ptype) |
|
|
|
{ |
|
|
|
case CB::PLUGIN_INTERNAL: |
|
|
|
case CB::PLUGIN_LV2: |
|
|
|
case CB::PLUGIN_SFZ: |
|
|
|
return false; |
|
|
|
default: |
|
|
|
return true; |
|
|
|
@@ -56,304 +57,333 @@ _CarlaCachedPluginInfo::_CarlaCachedPluginInfo() noexcept |
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------------------------- |
|
|
|
|
|
|
|
uint carla_get_cached_plugin_count(CB::PluginType ptype, const char* pluginPath) |
|
|
|
const CarlaCachedPluginInfo* get_cached_plugin_internal(const NativePluginDescriptor& desc) |
|
|
|
{ |
|
|
|
CARLA_SAFE_ASSERT_RETURN(isCachedPluginType(ptype), 0); |
|
|
|
carla_debug("carla_get_cached_plugin_count(%i:%s)", ptype, CB::PluginType2Str(ptype)); |
|
|
|
static CarlaCachedPluginInfo info; |
|
|
|
|
|
|
|
switch (ptype) |
|
|
|
{ |
|
|
|
case CB::PLUGIN_INTERNAL: { |
|
|
|
uint32_t count = 0; |
|
|
|
carla_get_native_plugins_data(&count); |
|
|
|
return count; |
|
|
|
} |
|
|
|
info.category = static_cast<CB::PluginCategory>(desc.category); |
|
|
|
info.hints = 0x0; |
|
|
|
|
|
|
|
if (desc.hints & NATIVE_PLUGIN_IS_RTSAFE) |
|
|
|
info.hints |= CB::PLUGIN_IS_RTSAFE; |
|
|
|
if (desc.hints & NATIVE_PLUGIN_IS_SYNTH) |
|
|
|
info.hints |= CB::PLUGIN_IS_SYNTH; |
|
|
|
if (desc.hints & NATIVE_PLUGIN_HAS_UI) |
|
|
|
info.hints |= CB::PLUGIN_HAS_CUSTOM_UI; |
|
|
|
if (desc.hints & NATIVE_PLUGIN_NEEDS_FIXED_BUFFERS) |
|
|
|
info.hints |= CB::PLUGIN_NEEDS_FIXED_BUFFERS; |
|
|
|
if (desc.hints & NATIVE_PLUGIN_NEEDS_UI_MAIN_THREAD) |
|
|
|
info.hints |= CB::PLUGIN_NEEDS_UI_MAIN_THREAD; |
|
|
|
if (desc.hints & NATIVE_PLUGIN_USES_MULTI_PROGS) |
|
|
|
info.hints |= CB::PLUGIN_USES_MULTI_PROGS; |
|
|
|
|
|
|
|
case CB::PLUGIN_LV2: { |
|
|
|
Lv2WorldClass& lv2World(Lv2WorldClass::getInstance()); |
|
|
|
lv2World.initIfNeeded(pluginPath); |
|
|
|
return lv2World.getPluginCount(); |
|
|
|
} |
|
|
|
|
|
|
|
default: |
|
|
|
return 0; |
|
|
|
} |
|
|
|
info.valid = true; |
|
|
|
info.audioIns = desc.audioIns; |
|
|
|
info.audioOuts = desc.audioOuts; |
|
|
|
info.midiIns = desc.midiIns; |
|
|
|
info.midiOuts = desc.midiOuts; |
|
|
|
info.parameterIns = desc.paramIns; |
|
|
|
info.parameterOuts = desc.paramOuts; |
|
|
|
info.name = desc.name; |
|
|
|
info.label = desc.label; |
|
|
|
info.maker = desc.maker; |
|
|
|
info.copyright = desc.copyright; |
|
|
|
return &info; |
|
|
|
} |
|
|
|
|
|
|
|
const CarlaCachedPluginInfo* carla_get_cached_plugin_info(CB::PluginType ptype, uint index) |
|
|
|
const CarlaCachedPluginInfo* get_cached_plugin_lv2(Lv2WorldClass& lv2World, Lilv::Plugin& lilvPlugin) |
|
|
|
{ |
|
|
|
carla_debug("carla_get_cached_plugin_info(%i:%s, %i)", ptype, CB::PluginType2Str(ptype), index); |
|
|
|
|
|
|
|
static CarlaCachedPluginInfo info; |
|
|
|
|
|
|
|
switch (ptype) |
|
|
|
info.valid = false; |
|
|
|
bool supported = true; |
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------------------------------- |
|
|
|
// text data |
|
|
|
|
|
|
|
{ |
|
|
|
case CB::PLUGIN_INTERNAL: { |
|
|
|
uint32_t count = 0; |
|
|
|
const NativePluginDescriptor* const descs(carla_get_native_plugins_data(&count)); |
|
|
|
CARLA_SAFE_ASSERT_BREAK(index < count); |
|
|
|
CARLA_SAFE_ASSERT_BREAK(descs != nullptr); |
|
|
|
static CarlaString suri, sname, smaker, slicense; |
|
|
|
suri.clear(); sname.clear(); smaker.clear(); slicense.clear(); |
|
|
|
|
|
|
|
const NativePluginDescriptor& desc(descs[index]); |
|
|
|
suri = lilvPlugin.get_uri().as_uri(); |
|
|
|
|
|
|
|
info.category = static_cast<CB::PluginCategory>(desc.category); |
|
|
|
info.hints = 0x0; |
|
|
|
|
|
|
|
if (desc.hints & NATIVE_PLUGIN_IS_RTSAFE) |
|
|
|
info.hints |= CB::PLUGIN_IS_RTSAFE; |
|
|
|
if (desc.hints & NATIVE_PLUGIN_IS_SYNTH) |
|
|
|
info.hints |= CB::PLUGIN_IS_SYNTH; |
|
|
|
if (desc.hints & NATIVE_PLUGIN_HAS_UI) |
|
|
|
info.hints |= CB::PLUGIN_HAS_CUSTOM_UI; |
|
|
|
if (desc.hints & NATIVE_PLUGIN_NEEDS_FIXED_BUFFERS) |
|
|
|
info.hints |= CB::PLUGIN_NEEDS_FIXED_BUFFERS; |
|
|
|
if (desc.hints & NATIVE_PLUGIN_NEEDS_UI_MAIN_THREAD) |
|
|
|
info.hints |= CB::PLUGIN_NEEDS_UI_MAIN_THREAD; |
|
|
|
if (desc.hints & NATIVE_PLUGIN_USES_MULTI_PROGS) |
|
|
|
info.hints |= CB::PLUGIN_USES_MULTI_PROGS; |
|
|
|
|
|
|
|
info.valid = true; |
|
|
|
info.audioIns = desc.audioIns; |
|
|
|
info.audioOuts = desc.audioOuts; |
|
|
|
info.midiIns = desc.midiIns; |
|
|
|
info.midiOuts = desc.midiOuts; |
|
|
|
info.parameterIns = desc.paramIns; |
|
|
|
info.parameterOuts = desc.paramOuts; |
|
|
|
info.name = desc.name; |
|
|
|
info.label = desc.label; |
|
|
|
info.maker = desc.maker; |
|
|
|
info.copyright = desc.copyright; |
|
|
|
return &info; |
|
|
|
} |
|
|
|
if (LilvNode* const nameNode = lilv_plugin_get_name(lilvPlugin.me)) |
|
|
|
{ |
|
|
|
if (const char* const name = lilv_node_as_string(nameNode)) |
|
|
|
sname = name; |
|
|
|
lilv_node_free(nameNode); |
|
|
|
} |
|
|
|
|
|
|
|
case CB::PLUGIN_LV2: { |
|
|
|
Lv2WorldClass& lv2World(Lv2WorldClass::getInstance()); |
|
|
|
if (const char* const author = lilvPlugin.get_author_name().as_string()) |
|
|
|
smaker = author; |
|
|
|
|
|
|
|
const LilvPlugin* const cPlugin(lv2World.getPluginFromIndex(index)); |
|
|
|
CARLA_SAFE_ASSERT_BREAK(cPlugin != nullptr); |
|
|
|
Lilv::Nodes licenseNodes(lilvPlugin.get_value(lv2World.doap_license)); |
|
|
|
|
|
|
|
Lilv::Plugin lilvPlugin(cPlugin); |
|
|
|
CARLA_SAFE_ASSERT_BREAK(lilvPlugin.get_uri().is_uri()); |
|
|
|
if (licenseNodes.size() > 0) |
|
|
|
{ |
|
|
|
if (const char* const license = licenseNodes.get_first().as_string()) |
|
|
|
slicense = license; |
|
|
|
} |
|
|
|
|
|
|
|
// features |
|
|
|
info.hints = 0x0; |
|
|
|
lilv_nodes_free(const_cast<LilvNodes*>(licenseNodes.me)); |
|
|
|
|
|
|
|
if (lilvPlugin.get_uis().size() > 0) |
|
|
|
info.hints |= CB::PLUGIN_HAS_CUSTOM_UI; |
|
|
|
info.name = sname.buffer(); |
|
|
|
info.label = suri.buffer(); |
|
|
|
info.maker = smaker.buffer(); |
|
|
|
info.copyright = slicense.buffer(); |
|
|
|
} |
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------------------------------- |
|
|
|
// features |
|
|
|
|
|
|
|
info.hints = 0x0; |
|
|
|
|
|
|
|
if (lilvPlugin.get_uis().size() > 0) |
|
|
|
info.hints |= CB::PLUGIN_HAS_CUSTOM_UI; |
|
|
|
|
|
|
|
{ |
|
|
|
Lilv::Nodes lilvRequiredFeatureNodes(lilvPlugin.get_required_features()); |
|
|
|
|
|
|
|
LILV_FOREACH(nodes, it, lilvRequiredFeatureNodes) |
|
|
|
{ |
|
|
|
Lilv::Nodes lilvFeatureNodes(lilvPlugin.get_supported_features()); |
|
|
|
Lilv::Node lilvFeatureNode(lilvRequiredFeatureNodes.get(it)); |
|
|
|
const char* const featureURI(lilvFeatureNode.as_uri()); |
|
|
|
CARLA_SAFE_ASSERT_CONTINUE(featureURI != nullptr); |
|
|
|
|
|
|
|
LILV_FOREACH(nodes, it, lilvFeatureNodes) |
|
|
|
if (! is_lv2_feature_supported(featureURI)) |
|
|
|
{ |
|
|
|
Lilv::Node lilvFeatureNode(lilvFeatureNodes.get(it)); |
|
|
|
const char* const featureURI(lilvFeatureNode.as_uri()); |
|
|
|
CARLA_SAFE_ASSERT_CONTINUE(featureURI != nullptr); |
|
|
|
if (std::strcmp(featureURI, LV2_DATA_ACCESS_URI) == 0 |
|
|
|
|| std::strcmp(featureURI, LV2_INSTANCE_ACCESS_URI) == 0) |
|
|
|
{ |
|
|
|
// we give a warning about this below |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
if (std::strcmp(featureURI, LV2_CORE__hardRTCapable) == 0) |
|
|
|
info.hints |= CB::PLUGIN_IS_RTSAFE; |
|
|
|
supported = false; |
|
|
|
carla_stderr("LV2 plugin '%s' requires unsupported feature '%s'", info.label, featureURI); |
|
|
|
} |
|
|
|
|
|
|
|
lilv_nodes_free(const_cast<LilvNodes*>(lilvFeatureNodes.me)); |
|
|
|
} |
|
|
|
|
|
|
|
// category |
|
|
|
info.category = CB::PLUGIN_CATEGORY_NONE; |
|
|
|
lilv_nodes_free(const_cast<LilvNodes*>(lilvRequiredFeatureNodes.me)); |
|
|
|
} |
|
|
|
|
|
|
|
{ |
|
|
|
Lilv::Nodes lilvSupportedFeatureNodes(lilvPlugin.get_supported_features()); |
|
|
|
|
|
|
|
LILV_FOREACH(nodes, it, lilvSupportedFeatureNodes) |
|
|
|
{ |
|
|
|
Lilv::Nodes typeNodes(lilvPlugin.get_value(lv2World.rdf_type)); |
|
|
|
Lilv::Node lilvFeatureNode(lilvSupportedFeatureNodes.get(it)); |
|
|
|
const char* const featureURI(lilvFeatureNode.as_uri()); |
|
|
|
CARLA_SAFE_ASSERT_CONTINUE(featureURI != nullptr); |
|
|
|
|
|
|
|
if (typeNodes.size() > 0) |
|
|
|
if (std::strcmp(featureURI, LV2_CORE__hardRTCapable) == 0) |
|
|
|
{ |
|
|
|
if (typeNodes.contains(lv2World.class_allpass)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_FILTER; |
|
|
|
if (typeNodes.contains(lv2World.class_amplifier)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_DYNAMICS; |
|
|
|
if (typeNodes.contains(lv2World.class_analyzer)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_UTILITY; |
|
|
|
if (typeNodes.contains(lv2World.class_bandpass)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_FILTER; |
|
|
|
if (typeNodes.contains(lv2World.class_chorus)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_MODULATOR; |
|
|
|
if (typeNodes.contains(lv2World.class_comb)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_FILTER; |
|
|
|
if (typeNodes.contains(lv2World.class_compressor)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_DYNAMICS; |
|
|
|
if (typeNodes.contains(lv2World.class_constant)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_OTHER; |
|
|
|
if (typeNodes.contains(lv2World.class_converter)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_UTILITY; |
|
|
|
if (typeNodes.contains(lv2World.class_delay)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_DELAY; |
|
|
|
if (typeNodes.contains(lv2World.class_distortion)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_DISTORTION; |
|
|
|
if (typeNodes.contains(lv2World.class_dynamics)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_DYNAMICS; |
|
|
|
if (typeNodes.contains(lv2World.class_eq)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_EQ; |
|
|
|
if (typeNodes.contains(lv2World.class_envelope)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_DYNAMICS; |
|
|
|
if (typeNodes.contains(lv2World.class_expander)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_DYNAMICS; |
|
|
|
if (typeNodes.contains(lv2World.class_filter)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_FILTER; |
|
|
|
if (typeNodes.contains(lv2World.class_flanger)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_MODULATOR; |
|
|
|
if (typeNodes.contains(lv2World.class_function)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_UTILITY; |
|
|
|
if (typeNodes.contains(lv2World.class_gate)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_DYNAMICS; |
|
|
|
if (typeNodes.contains(lv2World.class_generator)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_OTHER; |
|
|
|
if (typeNodes.contains(lv2World.class_highpass)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_FILTER; |
|
|
|
if (typeNodes.contains(lv2World.class_limiter)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_DYNAMICS; |
|
|
|
if (typeNodes.contains(lv2World.class_lowpass)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_FILTER; |
|
|
|
if (typeNodes.contains(lv2World.class_mixer)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_UTILITY; |
|
|
|
if (typeNodes.contains(lv2World.class_modulator)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_MODULATOR; |
|
|
|
if (typeNodes.contains(lv2World.class_multiEQ)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_EQ; |
|
|
|
if (typeNodes.contains(lv2World.class_oscillator)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_OTHER; |
|
|
|
if (typeNodes.contains(lv2World.class_paraEQ)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_EQ; |
|
|
|
if (typeNodes.contains(lv2World.class_phaser)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_MODULATOR; |
|
|
|
if (typeNodes.contains(lv2World.class_pitch)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_OTHER; |
|
|
|
if (typeNodes.contains(lv2World.class_reverb)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_DELAY; |
|
|
|
if (typeNodes.contains(lv2World.class_simulator)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_OTHER; |
|
|
|
if (typeNodes.contains(lv2World.class_spatial)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_OTHER; |
|
|
|
if (typeNodes.contains(lv2World.class_spectral)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_OTHER; |
|
|
|
if (typeNodes.contains(lv2World.class_utility)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_UTILITY; |
|
|
|
if (typeNodes.contains(lv2World.class_waveshaper)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_DISTORTION; |
|
|
|
if (typeNodes.contains(lv2World.class_instrument)) |
|
|
|
{ |
|
|
|
info.category = CB::PLUGIN_CATEGORY_SYNTH; |
|
|
|
info.hints |= CB::PLUGIN_IS_SYNTH; |
|
|
|
} |
|
|
|
info.hints |= CB::PLUGIN_IS_RTSAFE; |
|
|
|
} |
|
|
|
else if (std::strcmp(featureURI, LV2_DATA_ACCESS_URI) == 0 |
|
|
|
|| std::strcmp(featureURI, LV2_INSTANCE_ACCESS_URI) == 0) |
|
|
|
{ |
|
|
|
carla_stderr("LV2 plugin '%s' DSP wants UI feature '%s', ignoring this", info.label, featureURI); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
lilv_nodes_free(const_cast<LilvNodes*>(lilvSupportedFeatureNodes.me)); |
|
|
|
} |
|
|
|
|
|
|
|
lilv_nodes_free(const_cast<LilvNodes*>(typeNodes.me)); |
|
|
|
// ---------------------------------------------------------------------------------------------------------------- |
|
|
|
// category |
|
|
|
|
|
|
|
info.category = CB::PLUGIN_CATEGORY_NONE; |
|
|
|
|
|
|
|
{ |
|
|
|
Lilv::Nodes typeNodes(lilvPlugin.get_value(lv2World.rdf_type)); |
|
|
|
|
|
|
|
if (typeNodes.size() > 0) |
|
|
|
{ |
|
|
|
if (typeNodes.contains(lv2World.class_allpass)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_FILTER; |
|
|
|
if (typeNodes.contains(lv2World.class_amplifier)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_DYNAMICS; |
|
|
|
if (typeNodes.contains(lv2World.class_analyzer)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_UTILITY; |
|
|
|
if (typeNodes.contains(lv2World.class_bandpass)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_FILTER; |
|
|
|
if (typeNodes.contains(lv2World.class_chorus)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_MODULATOR; |
|
|
|
if (typeNodes.contains(lv2World.class_comb)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_FILTER; |
|
|
|
if (typeNodes.contains(lv2World.class_compressor)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_DYNAMICS; |
|
|
|
if (typeNodes.contains(lv2World.class_constant)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_OTHER; |
|
|
|
if (typeNodes.contains(lv2World.class_converter)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_UTILITY; |
|
|
|
if (typeNodes.contains(lv2World.class_delay)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_DELAY; |
|
|
|
if (typeNodes.contains(lv2World.class_distortion)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_DISTORTION; |
|
|
|
if (typeNodes.contains(lv2World.class_dynamics)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_DYNAMICS; |
|
|
|
if (typeNodes.contains(lv2World.class_eq)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_EQ; |
|
|
|
if (typeNodes.contains(lv2World.class_envelope)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_DYNAMICS; |
|
|
|
if (typeNodes.contains(lv2World.class_expander)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_DYNAMICS; |
|
|
|
if (typeNodes.contains(lv2World.class_filter)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_FILTER; |
|
|
|
if (typeNodes.contains(lv2World.class_flanger)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_MODULATOR; |
|
|
|
if (typeNodes.contains(lv2World.class_function)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_UTILITY; |
|
|
|
if (typeNodes.contains(lv2World.class_gate)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_DYNAMICS; |
|
|
|
if (typeNodes.contains(lv2World.class_generator)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_OTHER; |
|
|
|
if (typeNodes.contains(lv2World.class_highpass)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_FILTER; |
|
|
|
if (typeNodes.contains(lv2World.class_limiter)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_DYNAMICS; |
|
|
|
if (typeNodes.contains(lv2World.class_lowpass)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_FILTER; |
|
|
|
if (typeNodes.contains(lv2World.class_mixer)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_UTILITY; |
|
|
|
if (typeNodes.contains(lv2World.class_modulator)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_MODULATOR; |
|
|
|
if (typeNodes.contains(lv2World.class_multiEQ)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_EQ; |
|
|
|
if (typeNodes.contains(lv2World.class_oscillator)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_OTHER; |
|
|
|
if (typeNodes.contains(lv2World.class_paraEQ)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_EQ; |
|
|
|
if (typeNodes.contains(lv2World.class_phaser)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_MODULATOR; |
|
|
|
if (typeNodes.contains(lv2World.class_pitch)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_OTHER; |
|
|
|
if (typeNodes.contains(lv2World.class_reverb)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_DELAY; |
|
|
|
if (typeNodes.contains(lv2World.class_simulator)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_OTHER; |
|
|
|
if (typeNodes.contains(lv2World.class_spatial)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_OTHER; |
|
|
|
if (typeNodes.contains(lv2World.class_spectral)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_OTHER; |
|
|
|
if (typeNodes.contains(lv2World.class_utility)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_UTILITY; |
|
|
|
if (typeNodes.contains(lv2World.class_waveshaper)) |
|
|
|
info.category = CB::PLUGIN_CATEGORY_DISTORTION; |
|
|
|
if (typeNodes.contains(lv2World.class_instrument)) |
|
|
|
{ |
|
|
|
info.category = CB::PLUGIN_CATEGORY_SYNTH; |
|
|
|
info.hints |= CB::PLUGIN_IS_SYNTH; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// number data |
|
|
|
info.audioIns = 0; |
|
|
|
info.audioOuts = 0; |
|
|
|
info.midiIns = 0; |
|
|
|
info.midiOuts = 0; |
|
|
|
info.parameterIns = 0; |
|
|
|
info.parameterOuts = 0; |
|
|
|
lilv_nodes_free(const_cast<LilvNodes*>(typeNodes.me)); |
|
|
|
} |
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------------------------------- |
|
|
|
// number data |
|
|
|
|
|
|
|
for (uint i=0, count=lilvPlugin.get_num_ports(); i<count; ++i) |
|
|
|
{ |
|
|
|
Lilv::Port lilvPort(lilvPlugin.get_port_by_index(i)); |
|
|
|
|
|
|
|
bool isInput; |
|
|
|
|
|
|
|
for (uint i=0, count=lilvPlugin.get_num_ports(); i<count; ++i) |
|
|
|
/**/ if (lilvPort.is_a(lv2World.port_input)) |
|
|
|
{ |
|
|
|
isInput = true; |
|
|
|
} |
|
|
|
else if (lilvPort.is_a(lv2World.port_output)) |
|
|
|
{ |
|
|
|
isInput = false; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
Lilv::Port lilvPort(lilvPlugin.get_port_by_index(i)); |
|
|
|
const LilvNode* const symbolNode = lilvPort.get_symbol(); |
|
|
|
CARLA_SAFE_ASSERT_CONTINUE(symbolNode != nullptr && lilv_node_is_string(symbolNode)); |
|
|
|
|
|
|
|
bool isInput; |
|
|
|
const char* const symbol = lilv_node_as_string(symbolNode); |
|
|
|
CARLA_SAFE_ASSERT_CONTINUE(symbol != nullptr); |
|
|
|
|
|
|
|
/**/ if (lilvPort.is_a(lv2World.port_input)) |
|
|
|
isInput = true; |
|
|
|
else if (lilvPort.is_a(lv2World.port_output)) |
|
|
|
isInput = false; |
|
|
|
else |
|
|
|
carla_stderr("LV2 plugin '%s' port '%s' is neither input or output", info.label, symbol); |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
/**/ if (lilvPort.is_a(lv2World.port_control)) |
|
|
|
{ |
|
|
|
// skip some control ports |
|
|
|
if (lilvPort.has_property(lv2World.reportsLatency)) |
|
|
|
continue; |
|
|
|
|
|
|
|
/**/ if (lilvPort.is_a(lv2World.port_control)) |
|
|
|
if (LilvNode* const designationNode = lilv_port_get(lilvPort.parent, lilvPort.me, lv2World.designation.me)) |
|
|
|
{ |
|
|
|
// skip some control ports |
|
|
|
if (lilvPort.has_property(lv2World.reportsLatency)) |
|
|
|
continue; |
|
|
|
bool skip = false; |
|
|
|
|
|
|
|
if (LilvNode* const designationNode = lilv_port_get(lilvPort.parent, lilvPort.me, lv2World.designation.me)) |
|
|
|
if (const char* const designation = lilv_node_as_string(designationNode)) |
|
|
|
{ |
|
|
|
bool skip = false; |
|
|
|
|
|
|
|
if (const char* const designation = lilv_node_as_string(designationNode)) |
|
|
|
{ |
|
|
|
/**/ if (std::strcmp(designation, LV2_CORE__control) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_CORE__freeWheeling) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_CORE__latency) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_PARAMETERS__sampleRate) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_TIME__bar) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_TIME__barBeat) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_TIME__beat) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_TIME__beatUnit) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_TIME__beatsPerBar) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_TIME__beatsPerMinute) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_TIME__frame) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_TIME__framesPerSecond) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_TIME__speed) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_KXSTUDIO_PROPERTIES__TimePositionTicksPerBeat) == 0) |
|
|
|
skip = true; |
|
|
|
} |
|
|
|
|
|
|
|
lilv_node_free(designationNode); |
|
|
|
|
|
|
|
if (skip) |
|
|
|
continue; |
|
|
|
/**/ if (std::strcmp(designation, LV2_CORE__control) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_CORE__freeWheeling) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_CORE__latency) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_PARAMETERS__sampleRate) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_TIME__bar) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_TIME__barBeat) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_TIME__beat) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_TIME__beatUnit) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_TIME__beatsPerBar) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_TIME__beatsPerMinute) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_TIME__frame) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_TIME__framesPerSecond) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_TIME__speed) == 0) |
|
|
|
skip = true; |
|
|
|
else if (std::strcmp(designation, LV2_KXSTUDIO_PROPERTIES__TimePositionTicksPerBeat) == 0) |
|
|
|
skip = true; |
|
|
|
} |
|
|
|
|
|
|
|
if (isInput) |
|
|
|
++(info.parameterIns); |
|
|
|
else |
|
|
|
++(info.parameterOuts); |
|
|
|
} |
|
|
|
else if (lilvPort.is_a(lv2World.port_audio)) |
|
|
|
{ |
|
|
|
if (isInput) |
|
|
|
++(info.audioIns); |
|
|
|
else |
|
|
|
++(info.audioOuts); |
|
|
|
} |
|
|
|
else if (lilvPort.is_a(lv2World.port_cv)) |
|
|
|
{ |
|
|
|
lilv_node_free(designationNode); |
|
|
|
|
|
|
|
if (skip) |
|
|
|
continue; |
|
|
|
} |
|
|
|
else if (lilvPort.is_a(lv2World.port_atom)) |
|
|
|
{ |
|
|
|
Lilv::Nodes supportNodes(lilvPort.get_value(lv2World.atom_supports)); |
|
|
|
|
|
|
|
for (LilvIter *it = lilv_nodes_begin(supportNodes.me); ! lilv_nodes_is_end(supportNodes.me, it); it = lilv_nodes_next(supportNodes.me, it)) |
|
|
|
{ |
|
|
|
const Lilv::Node node(lilv_nodes_get(supportNodes.me, it)); |
|
|
|
CARLA_SAFE_ASSERT_CONTINUE(node.is_uri()); |
|
|
|
|
|
|
|
if (node.equals(lv2World.midi_event)) |
|
|
|
{ |
|
|
|
if (isInput) |
|
|
|
++(info.midiIns); |
|
|
|
else |
|
|
|
++(info.midiOuts); |
|
|
|
} |
|
|
|
} |
|
|
|
if (isInput) |
|
|
|
++(info.parameterIns); |
|
|
|
else |
|
|
|
++(info.parameterOuts); |
|
|
|
} |
|
|
|
else if (lilvPort.is_a(lv2World.port_audio)) |
|
|
|
{ |
|
|
|
if (isInput) |
|
|
|
++(info.audioIns); |
|
|
|
else |
|
|
|
++(info.audioOuts); |
|
|
|
} |
|
|
|
else if (lilvPort.is_a(lv2World.port_cv)) |
|
|
|
{ |
|
|
|
} |
|
|
|
else if (lilvPort.is_a(lv2World.port_atom)) |
|
|
|
{ |
|
|
|
Lilv::Nodes supportNodes(lilvPort.get_value(lv2World.atom_supports)); |
|
|
|
|
|
|
|
lilv_nodes_free(const_cast<LilvNodes*>(supportNodes.me)); |
|
|
|
} |
|
|
|
else if (lilvPort.is_a(lv2World.port_event)) |
|
|
|
for (LilvIter *it = lilv_nodes_begin(supportNodes.me); ! lilv_nodes_is_end(supportNodes.me, it); it = lilv_nodes_next(supportNodes.me, it)) |
|
|
|
{ |
|
|
|
if (lilvPort.supports_event(lv2World.midi_event)) |
|
|
|
const Lilv::Node node(lilv_nodes_get(supportNodes.me, it)); |
|
|
|
CARLA_SAFE_ASSERT_CONTINUE(node.is_uri()); |
|
|
|
|
|
|
|
if (node.equals(lv2World.midi_event)) |
|
|
|
{ |
|
|
|
if (isInput) |
|
|
|
++(info.midiIns); |
|
|
|
@@ -361,7 +391,12 @@ const CarlaCachedPluginInfo* carla_get_cached_plugin_info(CB::PluginType ptype, |
|
|
|
++(info.midiOuts); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (lilvPort.is_a(lv2World.port_midi)) |
|
|
|
|
|
|
|
lilv_nodes_free(const_cast<LilvNodes*>(supportNodes.me)); |
|
|
|
} |
|
|
|
else if (lilvPort.is_a(lv2World.port_event)) |
|
|
|
{ |
|
|
|
if (lilvPort.supports_event(lv2World.midi_event)) |
|
|
|
{ |
|
|
|
if (isInput) |
|
|
|
++(info.midiIns); |
|
|
|
@@ -369,59 +404,105 @@ const CarlaCachedPluginInfo* carla_get_cached_plugin_info(CB::PluginType ptype, |
|
|
|
++(info.midiOuts); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (lilvPort.is_a(lv2World.port_midi)) |
|
|
|
{ |
|
|
|
if (isInput) |
|
|
|
++(info.midiIns); |
|
|
|
else |
|
|
|
++(info.midiOuts); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
const LilvNode* const symbolNode = lilvPort.get_symbol(); |
|
|
|
CARLA_SAFE_ASSERT_CONTINUE(symbolNode != nullptr && lilv_node_is_string(symbolNode)); |
|
|
|
|
|
|
|
// text data |
|
|
|
static CarlaString suri, sname, smaker, slicense; |
|
|
|
suri.clear(); sname.clear(); smaker.clear(); slicense.clear(); |
|
|
|
|
|
|
|
suri = lilvPlugin.get_uri().as_uri(); |
|
|
|
const char* const symbol = lilv_node_as_string(symbolNode); |
|
|
|
CARLA_SAFE_ASSERT_CONTINUE(symbol != nullptr); |
|
|
|
|
|
|
|
if (LilvNode* const nameNode = lilv_plugin_get_name(lilvPlugin.me)) |
|
|
|
{ |
|
|
|
if (const char* const name = lilv_node_as_string(nameNode)) |
|
|
|
sname = name; |
|
|
|
lilv_node_free(nameNode); |
|
|
|
supported = false; |
|
|
|
carla_stderr("LV2 plugin '%s' port '%s' is required but has unsupported type", info.label, symbol); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (const char* const author = lilvPlugin.get_author_name().as_string()) |
|
|
|
smaker = author; |
|
|
|
if (supported) |
|
|
|
info.valid = true; |
|
|
|
|
|
|
|
Lilv::Nodes licenseNodes(lilvPlugin.get_value(lv2World.doap_license)); |
|
|
|
return &info; |
|
|
|
} |
|
|
|
|
|
|
|
if (licenseNodes.size() > 0) |
|
|
|
{ |
|
|
|
if (const char* const license = licenseNodes.get_first().as_string()) |
|
|
|
slicense = license; |
|
|
|
} |
|
|
|
const CarlaCachedPluginInfo* get_cached_plugin_sfz() |
|
|
|
{ |
|
|
|
static CarlaCachedPluginInfo info; |
|
|
|
return &info; |
|
|
|
} |
|
|
|
|
|
|
|
lilv_nodes_free(const_cast<LilvNodes*>(licenseNodes.me)); |
|
|
|
// ------------------------------------------------------------------------------------------------------------------- |
|
|
|
|
|
|
|
info.valid = true; |
|
|
|
info.name = sname; |
|
|
|
info.label = suri; |
|
|
|
info.maker = smaker; |
|
|
|
info.copyright = slicense; |
|
|
|
uint carla_get_cached_plugin_count(CB::PluginType ptype, const char* pluginPath) |
|
|
|
{ |
|
|
|
CARLA_SAFE_ASSERT_RETURN(isCachedPluginType(ptype), 0); |
|
|
|
carla_debug("carla_get_cached_plugin_count(%i:%s)", ptype, CB::PluginType2Str(ptype)); |
|
|
|
|
|
|
|
switch (ptype) |
|
|
|
{ |
|
|
|
case CB::PLUGIN_INTERNAL: { |
|
|
|
uint32_t count = 0; |
|
|
|
carla_get_native_plugins_data(&count); |
|
|
|
return count; |
|
|
|
} |
|
|
|
|
|
|
|
case CB::PLUGIN_LV2: { |
|
|
|
Lv2WorldClass& lv2World(Lv2WorldClass::getInstance()); |
|
|
|
lv2World.initIfNeeded(pluginPath); |
|
|
|
return lv2World.getPluginCount(); |
|
|
|
} |
|
|
|
|
|
|
|
case CB::PLUGIN_SFZ: { |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
default: |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const CarlaCachedPluginInfo* carla_get_cached_plugin_info(CB::PluginType ptype, uint index) |
|
|
|
{ |
|
|
|
carla_debug("carla_get_cached_plugin_info(%i:%s, %i)", ptype, CB::PluginType2Str(ptype), index); |
|
|
|
|
|
|
|
switch (ptype) |
|
|
|
{ |
|
|
|
case CB::PLUGIN_INTERNAL: { |
|
|
|
uint32_t count = 0; |
|
|
|
const NativePluginDescriptor* const descs(carla_get_native_plugins_data(&count)); |
|
|
|
CARLA_SAFE_ASSERT_BREAK(index < count); |
|
|
|
CARLA_SAFE_ASSERT_BREAK(descs != nullptr); |
|
|
|
|
|
|
|
const NativePluginDescriptor& desc(descs[index]); |
|
|
|
return get_cached_plugin_internal(desc); |
|
|
|
} |
|
|
|
|
|
|
|
return &info; |
|
|
|
case CB::PLUGIN_LV2: { |
|
|
|
Lv2WorldClass& lv2World(Lv2WorldClass::getInstance()); |
|
|
|
|
|
|
|
const LilvPlugin* const cPlugin(lv2World.getPluginFromIndex(index)); |
|
|
|
CARLA_SAFE_ASSERT_BREAK(cPlugin != nullptr); |
|
|
|
|
|
|
|
Lilv::Plugin lilvPlugin(cPlugin); |
|
|
|
CARLA_SAFE_ASSERT_BREAK(lilvPlugin.get_uri().is_uri()); |
|
|
|
|
|
|
|
return get_cached_plugin_lv2(lv2World, lilvPlugin); |
|
|
|
} |
|
|
|
|
|
|
|
case CB::PLUGIN_SFZ: { |
|
|
|
return get_cached_plugin_sfz(); |
|
|
|
} |
|
|
|
|
|
|
|
default: |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
info.valid = true; |
|
|
|
info.category = CB::PLUGIN_CATEGORY_NONE; |
|
|
|
info.hints = 0x0; |
|
|
|
info.audioIns = 0; |
|
|
|
info.audioOuts = 0; |
|
|
|
info.midiIns = 0; |
|
|
|
info.midiOuts = 0; |
|
|
|
info.parameterIns = 0; |
|
|
|
info.parameterOuts = 0; |
|
|
|
info.name = gNullCharPtr; |
|
|
|
info.label = gNullCharPtr; |
|
|
|
info.maker = gNullCharPtr; |
|
|
|
info.copyright = gNullCharPtr; |
|
|
|
static CarlaCachedPluginInfo info; |
|
|
|
return &info; |
|
|
|
} |
|
|
|
|
|
|
|
|