1 // SPDX-FileCopyrightText: 2011-2024 Filipe Coelho <falktx@falktx.com>
2 // SPDX-License-Identifier: GPL-2.0-or-later
4 #include "CarlaUtils.h"
6 #include "CarlaNative.h"
7 #include "CarlaString.hpp"
8 #include "CarlaBackendUtils.hpp"
9 #include "CarlaLv2Utils.hpp"
11 #ifndef STATIC_PLUGIN_TARGET
13 # include "water/containers/Array.h"
17 # include "CarlaJsfxUtils.hpp"
20 #include "water/files/File.h"
22 namespace CB
= CARLA_BACKEND_NAMESPACE
;
24 // -------------------------------------------------------------------------------------------------------------------
26 static const char* const gCachedPluginsNullCharPtr
= "";
28 static bool isCachedPluginType(const CB::PluginType ptype
)
32 case CB::PLUGIN_INTERNAL
:
43 // -------------------------------------------------------------------------------------------------------------------
45 _CarlaCachedPluginInfo::_CarlaCachedPluginInfo() noexcept
47 category(CB::PLUGIN_CATEGORY_NONE
),
57 name(gCachedPluginsNullCharPtr
),
58 label(gCachedPluginsNullCharPtr
),
59 maker(gCachedPluginsNullCharPtr
),
60 copyright(gCachedPluginsNullCharPtr
) {}
62 // -------------------------------------------------------------------------------------------------------------------
65 static std::vector
<water::File
> gSFZs
;
67 static void findSFZs(const char* const sfzPaths
)
71 CARLA_SAFE_ASSERT_RETURN(sfzPaths
!= nullptr,);
73 if (sfzPaths
[0] == '\0')
76 const water::StringArray
splitPaths(water::StringArray::fromTokens(sfzPaths
, CARLA_OS_SPLIT_STR
, ""));
78 for (water::String
*it
= splitPaths
.begin(), *end
= splitPaths
.end(); it
!= end
; ++it
)
80 std::vector
<water::File
> results
;
82 if (water::File(it
->toRawUTF8()).findChildFiles(results
, water::File::findFiles
|water::File::ignoreHiddenFiles
, true, "*.sfz") > 0)
84 gSFZs
.reserve(gSFZs
.size() + results
.size());
85 gSFZs
.insert(gSFZs
.end(), results
.begin(), results
.end());
91 // -------------------------------------------------------------------------------------------------------------------
94 static std::vector
<CB::CarlaJsfxUnit
> gJSFXs
;
96 static void findJSFXs(const char* const jsfxPaths
)
100 CARLA_SAFE_ASSERT_RETURN(jsfxPaths
!= nullptr,);
102 if (jsfxPaths
[0] == '\0')
105 const water::StringArray
splitPaths(water::StringArray::fromTokens(jsfxPaths
, CARLA_OS_SPLIT_STR
, ""));
107 for (water::String
*it
= splitPaths
.begin(), *end
= splitPaths
.end(); it
!= end
; ++it
)
109 std::vector
<water::File
> results
;
110 const water::File
path(it
->toRawUTF8());
112 if (path
.findChildFiles(results
, water::File::findFiles
|water::File::ignoreHiddenFiles
, true, "*") > 0)
114 gJSFXs
.reserve(gJSFXs
.size() + results
.size());
116 for (std::vector
<water::File
>::iterator it2
=results
.begin(), end2
=results
.end(); it2
!= end2
; ++it2
)
118 const water::File
& file(*it2
);
119 const water::String fileExt
= file
.getFileExtension();
120 if (fileExt
.isEmpty() || fileExt
.equalsIgnoreCase(".jsfx"))
121 gJSFXs
.push_back(CB::CarlaJsfxUnit(path
, file
));
128 // -------------------------------------------------------------------------------------------------------------------
130 static const CarlaCachedPluginInfo
* get_cached_plugin_internal(const NativePluginDescriptor
& desc
)
132 static CarlaCachedPluginInfo info
;
134 info
.category
= static_cast<CB::PluginCategory
>(desc
.category
);
137 if (desc
.hints
& NATIVE_PLUGIN_IS_RTSAFE
)
138 info
.hints
|= CB::PLUGIN_IS_RTSAFE
;
139 if (desc
.hints
& NATIVE_PLUGIN_IS_SYNTH
)
140 info
.hints
|= CB::PLUGIN_IS_SYNTH
;
141 if (desc
.hints
& NATIVE_PLUGIN_HAS_UI
)
142 info
.hints
|= CB::PLUGIN_HAS_CUSTOM_UI
;
143 if (desc
.hints
& NATIVE_PLUGIN_HAS_INLINE_DISPLAY
)
144 info
.hints
|= CB::PLUGIN_HAS_INLINE_DISPLAY
;
145 if (desc
.hints
& NATIVE_PLUGIN_NEEDS_FIXED_BUFFERS
)
146 info
.hints
|= CB::PLUGIN_NEEDS_FIXED_BUFFERS
;
147 if (desc
.hints
& NATIVE_PLUGIN_NEEDS_UI_MAIN_THREAD
)
148 info
.hints
|= CB::PLUGIN_NEEDS_UI_MAIN_THREAD
;
149 if (desc
.hints
& NATIVE_PLUGIN_USES_MULTI_PROGS
)
150 info
.hints
|= CB::PLUGIN_USES_MULTI_PROGS
;
153 info
.audioIns
= desc
.audioIns
;
154 info
.audioOuts
= desc
.audioOuts
;
155 info
.cvIns
= desc
.cvIns
;
156 info
.cvOuts
= desc
.cvOuts
;
157 info
.midiIns
= desc
.midiIns
;
158 info
.midiOuts
= desc
.midiOuts
;
159 info
.parameterIns
= desc
.paramIns
;
160 info
.parameterOuts
= desc
.paramOuts
;
161 info
.name
= desc
.name
;
162 info
.label
= desc
.label
;
163 info
.maker
= desc
.maker
;
164 info
.copyright
= desc
.copyright
;
168 // -------------------------------------------------------------------------------------------------------------------
170 static const CarlaCachedPluginInfo
* get_cached_plugin_lv2(Lv2WorldClass
& lv2World
, Lilv::Plugin
& lilvPlugin
)
172 static CarlaCachedPluginInfo info
;
175 bool supported
= true;
177 // ----------------------------------------------------------------------------------------------------------------
181 static CarlaString suri
, sname
, smaker
, slicense
;
182 suri
.clear(); sname
.clear(); smaker
.clear(); slicense
.clear();
184 suri
= lilvPlugin
.get_uri().as_uri();
186 if (char* const bundle
= lilv_file_uri_parse(lilvPlugin
.get_bundle_uri().as_uri(), nullptr))
188 const water::File
fbundle(bundle
);
189 suri
= (fbundle
.getFileName() + CARLA_OS_SEP
).toRawUTF8() + suri
;
194 suri
= CARLA_OS_SEP_STR
+ suri
;
197 #if 0 // def HAVE_FLUIDSYNTH
198 // If we have fluidsynth support built-in, loading these plugins will lead to issues
199 if (suri
== "urn:ardour:a-fluidsynth")
201 if (suri
== "http://calf.sourceforge.net/plugins/Fluidsynth")
205 if (LilvNode
* const nameNode
= lilv_plugin_get_name(lilvPlugin
.me
))
207 if (const char* const name
= lilv_node_as_string(nameNode
))
209 lilv_node_free(nameNode
);
212 if (LilvNode
* const authorNode
= lilv_plugin_get_author_name(lilvPlugin
.me
))
214 if (const char* const author
= lilv_node_as_string(authorNode
))
216 lilv_node_free(authorNode
);
219 Lilv::Nodes
licenseNodes(lilvPlugin
.get_value(lv2World
.doap_license
));
221 if (licenseNodes
.size() > 0)
223 if (LilvNode
* const licenseNode
= lilv_nodes_get_first(licenseNodes
.me
))
225 if (const char* const license
= lilv_node_as_string(licenseNode
))
227 // lilv_node_free(licenseNode);
231 lilv_nodes_free(const_cast<LilvNodes
*>(licenseNodes
.me
));
233 info
.name
= sname
.buffer();
234 info
.label
= suri
.buffer();
235 info
.maker
= smaker
.buffer();
236 info
.copyright
= slicense
.buffer();
239 // ----------------------------------------------------------------------------------------------------------------
245 Lilv::UIs
lilvUIs(lilvPlugin
.get_uis());
247 if (lilvUIs
.size() > 0)
249 info
.hints
|= CB::PLUGIN_HAS_CUSTOM_UI
;
251 LILV_FOREACH(uis
, it
, lilvUIs
)
253 Lilv::UI
lilvUI(lilvUIs
.get(it
));
254 lv2World
.load_resource(lilvUI
.get_uri());
256 #if defined(CARLA_OS_MAC)
257 if (lilvUI
.is_a(lv2World
.ui_cocoa
))
258 #elif defined(CARLA_OS_WIN)
259 if (lilvUI
.is_a(lv2World
.ui_windows
))
260 #elif defined(HAVE_X11)
261 if (lilvUI
.is_a(lv2World
.ui_x11
))
266 info
.hints
|= CB::PLUGIN_HAS_CUSTOM_EMBED_UI
;
271 #ifdef CARLA_OS_LINUX
272 else if (lilvPlugin
.get_modgui_resources_directory().as_uri() != nullptr)
273 info
.hints
|= CB::PLUGIN_HAS_CUSTOM_UI
;
276 lilv_nodes_free(const_cast<LilvNodes
*>(lilvUIs
.me
));
280 Lilv::Nodes
lilvRequiredFeatureNodes(lilvPlugin
.get_required_features());
282 LILV_FOREACH(nodes
, it
, lilvRequiredFeatureNodes
)
284 Lilv::Node
lilvFeatureNode(lilvRequiredFeatureNodes
.get(it
));
285 const char* const featureURI(lilvFeatureNode
.as_uri());
286 CARLA_SAFE_ASSERT_CONTINUE(featureURI
!= nullptr);
288 if (! is_lv2_feature_supported(featureURI
))
290 if (std::strcmp(featureURI
, LV2_DATA_ACCESS_URI
) == 0
291 || std::strcmp(featureURI
, LV2_INSTANCE_ACCESS_URI
) == 0)
293 // we give a warning about this below
298 carla_stderr("LV2 plugin '%s' requires unsupported feature '%s'", info
.label
, featureURI
);
302 lilv_nodes_free(const_cast<LilvNodes
*>(lilvRequiredFeatureNodes
.me
));
306 Lilv::Nodes
lilvSupportedFeatureNodes(lilvPlugin
.get_supported_features());
308 LILV_FOREACH(nodes
, it
, lilvSupportedFeatureNodes
)
310 Lilv::Node
lilvFeatureNode(lilvSupportedFeatureNodes
.get(it
));
311 const char* const featureURI(lilvFeatureNode
.as_uri());
312 CARLA_SAFE_ASSERT_CONTINUE(featureURI
!= nullptr);
314 /**/ if (std::strcmp(featureURI
, LV2_CORE__hardRTCapable
) == 0)
316 info
.hints
|= CB::PLUGIN_IS_RTSAFE
;
318 else if (std::strcmp(featureURI
, LV2_INLINEDISPLAY__queue_draw
) == 0)
320 info
.hints
|= CB::PLUGIN_HAS_INLINE_DISPLAY
;
322 else if (std::strcmp(featureURI
, LV2_DATA_ACCESS_URI
) == 0
323 || std::strcmp(featureURI
, LV2_INSTANCE_ACCESS_URI
) == 0)
325 carla_stderr("LV2 plugin '%s' DSP wants UI feature '%s', ignoring this", info
.label
, featureURI
);
329 lilv_nodes_free(const_cast<LilvNodes
*>(lilvSupportedFeatureNodes
.me
));
332 // ----------------------------------------------------------------------------------------------------------------
335 info
.category
= CB::PLUGIN_CATEGORY_NONE
;
338 Lilv::Nodes
typeNodes(lilvPlugin
.get_value(lv2World
.rdf_type
));
340 if (typeNodes
.size() > 0)
342 if (typeNodes
.contains(lv2World
.class_allpass
))
343 info
.category
= CB::PLUGIN_CATEGORY_FILTER
;
344 if (typeNodes
.contains(lv2World
.class_amplifier
))
345 info
.category
= CB::PLUGIN_CATEGORY_DYNAMICS
;
346 if (typeNodes
.contains(lv2World
.class_analyzer
))
347 info
.category
= CB::PLUGIN_CATEGORY_UTILITY
;
348 if (typeNodes
.contains(lv2World
.class_bandpass
))
349 info
.category
= CB::PLUGIN_CATEGORY_FILTER
;
350 if (typeNodes
.contains(lv2World
.class_chorus
))
351 info
.category
= CB::PLUGIN_CATEGORY_MODULATOR
;
352 if (typeNodes
.contains(lv2World
.class_comb
))
353 info
.category
= CB::PLUGIN_CATEGORY_FILTER
;
354 if (typeNodes
.contains(lv2World
.class_compressor
))
355 info
.category
= CB::PLUGIN_CATEGORY_DYNAMICS
;
356 if (typeNodes
.contains(lv2World
.class_constant
))
357 info
.category
= CB::PLUGIN_CATEGORY_OTHER
;
358 if (typeNodes
.contains(lv2World
.class_converter
))
359 info
.category
= CB::PLUGIN_CATEGORY_UTILITY
;
360 if (typeNodes
.contains(lv2World
.class_delay
))
361 info
.category
= CB::PLUGIN_CATEGORY_DELAY
;
362 if (typeNodes
.contains(lv2World
.class_distortion
))
363 info
.category
= CB::PLUGIN_CATEGORY_DISTORTION
;
364 if (typeNodes
.contains(lv2World
.class_dynamics
))
365 info
.category
= CB::PLUGIN_CATEGORY_DYNAMICS
;
366 if (typeNodes
.contains(lv2World
.class_eq
))
367 info
.category
= CB::PLUGIN_CATEGORY_EQ
;
368 if (typeNodes
.contains(lv2World
.class_envelope
))
369 info
.category
= CB::PLUGIN_CATEGORY_DYNAMICS
;
370 if (typeNodes
.contains(lv2World
.class_expander
))
371 info
.category
= CB::PLUGIN_CATEGORY_DYNAMICS
;
372 if (typeNodes
.contains(lv2World
.class_filter
))
373 info
.category
= CB::PLUGIN_CATEGORY_FILTER
;
374 if (typeNodes
.contains(lv2World
.class_flanger
))
375 info
.category
= CB::PLUGIN_CATEGORY_MODULATOR
;
376 if (typeNodes
.contains(lv2World
.class_function
))
377 info
.category
= CB::PLUGIN_CATEGORY_UTILITY
;
378 if (typeNodes
.contains(lv2World
.class_gate
))
379 info
.category
= CB::PLUGIN_CATEGORY_DYNAMICS
;
380 if (typeNodes
.contains(lv2World
.class_generator
))
381 info
.category
= CB::PLUGIN_CATEGORY_OTHER
;
382 if (typeNodes
.contains(lv2World
.class_highpass
))
383 info
.category
= CB::PLUGIN_CATEGORY_FILTER
;
384 if (typeNodes
.contains(lv2World
.class_limiter
))
385 info
.category
= CB::PLUGIN_CATEGORY_DYNAMICS
;
386 if (typeNodes
.contains(lv2World
.class_lowpass
))
387 info
.category
= CB::PLUGIN_CATEGORY_FILTER
;
388 if (typeNodes
.contains(lv2World
.class_mixer
))
389 info
.category
= CB::PLUGIN_CATEGORY_UTILITY
;
390 if (typeNodes
.contains(lv2World
.class_modulator
))
391 info
.category
= CB::PLUGIN_CATEGORY_MODULATOR
;
392 if (typeNodes
.contains(lv2World
.class_multiEQ
))
393 info
.category
= CB::PLUGIN_CATEGORY_EQ
;
394 if (typeNodes
.contains(lv2World
.class_oscillator
))
395 info
.category
= CB::PLUGIN_CATEGORY_OTHER
;
396 if (typeNodes
.contains(lv2World
.class_paraEQ
))
397 info
.category
= CB::PLUGIN_CATEGORY_EQ
;
398 if (typeNodes
.contains(lv2World
.class_phaser
))
399 info
.category
= CB::PLUGIN_CATEGORY_MODULATOR
;
400 if (typeNodes
.contains(lv2World
.class_pitch
))
401 info
.category
= CB::PLUGIN_CATEGORY_OTHER
;
402 if (typeNodes
.contains(lv2World
.class_reverb
))
403 info
.category
= CB::PLUGIN_CATEGORY_DELAY
;
404 if (typeNodes
.contains(lv2World
.class_simulator
))
405 info
.category
= CB::PLUGIN_CATEGORY_OTHER
;
406 if (typeNodes
.contains(lv2World
.class_spatial
))
407 info
.category
= CB::PLUGIN_CATEGORY_OTHER
;
408 if (typeNodes
.contains(lv2World
.class_spectral
))
409 info
.category
= CB::PLUGIN_CATEGORY_OTHER
;
410 if (typeNodes
.contains(lv2World
.class_utility
))
411 info
.category
= CB::PLUGIN_CATEGORY_UTILITY
;
412 if (typeNodes
.contains(lv2World
.class_waveshaper
))
413 info
.category
= CB::PLUGIN_CATEGORY_DISTORTION
;
414 if (typeNodes
.contains(lv2World
.class_instrument
))
416 info
.category
= CB::PLUGIN_CATEGORY_SYNTH
;
417 info
.hints
|= CB::PLUGIN_IS_SYNTH
;
421 lilv_nodes_free(const_cast<LilvNodes
*>(typeNodes
.me
));
424 // ----------------------------------------------------------------------------------------------------------------
433 info
.parameterIns
= 0;
434 info
.parameterOuts
= 0;
436 for (uint i
=0, count
=lilvPlugin
.get_num_ports(); i
<count
; ++i
)
438 Lilv::Port
lilvPort(lilvPlugin
.get_port_by_index(i
));
442 /**/ if (lilvPort
.is_a(lv2World
.port_input
))
446 else if (lilvPort
.is_a(lv2World
.port_output
))
452 const LilvNode
* const symbolNode
= lilvPort
.get_symbol();
453 CARLA_SAFE_ASSERT_CONTINUE(symbolNode
!= nullptr && lilv_node_is_string(symbolNode
));
455 const char* const symbol
= lilv_node_as_string(symbolNode
);
456 CARLA_SAFE_ASSERT_CONTINUE(symbol
!= nullptr);
458 carla_stderr("LV2 plugin '%s' port '%s' is neither input or output", info
.label
, symbol
);
462 /**/ if (lilvPort
.is_a(lv2World
.port_control
))
464 // skip some control ports
465 if (lilvPort
.has_property(lv2World
.reportsLatency
))
468 if (LilvNode
* const designationNode
= lilv_port_get(lilvPort
.parent
, lilvPort
.me
, lv2World
.designation
.me
))
472 if (const char* const designation
= lilv_node_as_string(designationNode
))
474 /**/ if (std::strcmp(designation
, LV2_CORE__control
) == 0)
476 else if (std::strcmp(designation
, LV2_CORE__freeWheeling
) == 0)
478 else if (std::strcmp(designation
, LV2_CORE__latency
) == 0)
480 else if (std::strcmp(designation
, LV2_PARAMETERS__sampleRate
) == 0)
482 else if (std::strcmp(designation
, LV2_TIME__bar
) == 0)
484 else if (std::strcmp(designation
, LV2_TIME__barBeat
) == 0)
486 else if (std::strcmp(designation
, LV2_TIME__beat
) == 0)
488 else if (std::strcmp(designation
, LV2_TIME__beatUnit
) == 0)
490 else if (std::strcmp(designation
, LV2_TIME__beatsPerBar
) == 0)
492 else if (std::strcmp(designation
, LV2_TIME__beatsPerMinute
) == 0)
494 else if (std::strcmp(designation
, LV2_TIME__frame
) == 0)
496 else if (std::strcmp(designation
, LV2_TIME__framesPerSecond
) == 0)
498 else if (std::strcmp(designation
, LV2_TIME__speed
) == 0)
500 else if (std::strcmp(designation
, LV2_KXSTUDIO_PROPERTIES__TimePositionTicksPerBeat
) == 0)
504 lilv_node_free(designationNode
);
511 ++(info
.parameterIns
);
513 ++(info
.parameterOuts
);
515 else if (lilvPort
.is_a(lv2World
.port_audio
))
522 else if (lilvPort
.is_a(lv2World
.port_cv
))
529 else if (lilvPort
.is_a(lv2World
.port_atom
))
531 Lilv::Nodes
supportNodes(lilvPort
.get_value(lv2World
.atom_supports
));
533 for (LilvIter
*it
= lilv_nodes_begin(supportNodes
.me
); ! lilv_nodes_is_end(supportNodes
.me
, it
); it
= lilv_nodes_next(supportNodes
.me
, it
))
535 const Lilv::Node
node(lilv_nodes_get(supportNodes
.me
, it
));
536 CARLA_SAFE_ASSERT_CONTINUE(node
.is_uri());
538 if (node
.equals(lv2World
.midi_event
))
547 lilv_nodes_free(const_cast<LilvNodes
*>(supportNodes
.me
));
549 else if (lilvPort
.is_a(lv2World
.port_event
))
551 if (lilvPort
.supports_event(lv2World
.midi_event
))
559 else if (lilvPort
.is_a(lv2World
.port_midi
))
568 const LilvNode
* const symbolNode
= lilvPort
.get_symbol();
569 CARLA_SAFE_ASSERT_CONTINUE(symbolNode
!= nullptr && lilv_node_is_string(symbolNode
));
571 const char* const symbol
= lilv_node_as_string(symbolNode
);
572 CARLA_SAFE_ASSERT_CONTINUE(symbol
!= nullptr);
575 carla_stderr("LV2 plugin '%s' port '%s' is required but has unsupported type", info
.label
, symbol
);
585 // -------------------------------------------------------------------------------------------------------------------
588 static const CarlaCachedPluginInfo
* get_cached_plugin_sfz(const water::File
& file
)
590 static CarlaCachedPluginInfo info
;
592 static CarlaString name
, filename
;
594 name
= file
.getFileNameWithoutExtension().toRawUTF8();
595 name
.replace('_',' ');
597 filename
= file
.getFullPathName().toRawUTF8();
599 info
.category
= CB::PLUGIN_CATEGORY_SYNTH
;
600 info
.hints
= CB::PLUGIN_IS_SYNTH
;
601 // CB::PLUGIN_IS_RTSAFE
610 info
.parameterIns
= 0;
611 info
.parameterOuts
= 1;
613 info
.name
= name
.buffer();
614 info
.label
= filename
.buffer();
615 info
.maker
= gCachedPluginsNullCharPtr
;
616 info
.copyright
= gCachedPluginsNullCharPtr
;
621 // -------------------------------------------------------------------------------------------------------------------
624 static const CarlaCachedPluginInfo
* get_cached_plugin_jsfx(const CB::CarlaJsfxUnit
& unit
)
626 static CarlaCachedPluginInfo info
;
628 ysfx_config_u
config(ysfx_config_new());
630 const water::String rootPath
= unit
.getRootPath();
631 const water::String filePath
= unit
.getFilePath();
633 ysfx_register_builtin_audio_formats(config
.get());
634 ysfx_set_import_root(config
.get(), rootPath
.toRawUTF8());
635 ysfx_guess_file_roots(config
.get(), filePath
.toRawUTF8());
636 ysfx_set_log_reporter(config
.get(), &CB::CarlaJsfxLogging::logErrorsOnly
);
638 ysfx_u
effect(ysfx_new(config
.get()));
640 if (! ysfx_load_file(effect
.get(), filePath
.toRawUTF8(), 0))
646 // plugins with neither @block nor @sample are valid, but they are useless
647 // also use this as a sanity check against misdetected files
648 // since JSFX parsing is so permissive, it might accept lambda text files
649 if (! ysfx_has_section(effect
.get(), ysfx_section_block
) &&
650 ! ysfx_has_section(effect
.get(), ysfx_section_sample
))
656 static CarlaString name
, label
, maker
;
657 label
= unit
.getFileId().toRawUTF8();
658 name
= ysfx_get_name(effect
.get());
659 maker
= ysfx_get_author(effect
.get());
663 info
.category
= CB::CarlaJsfxCategories::getFromEffect(effect
.get());
665 info
.audioIns
= ysfx_get_num_inputs(effect
.get());
666 info
.audioOuts
= ysfx_get_num_outputs(effect
.get());
674 info
.parameterIns
= 0;
675 info
.parameterOuts
= 0;
676 for (uint32_t sliderIndex
= 0; sliderIndex
< ysfx_max_sliders
; ++sliderIndex
)
678 if (ysfx_slider_exists(effect
.get(), sliderIndex
))
684 #if 0 // TODO(jsfx) when supporting custom graphics
685 if (ysfx_has_section(effect
.get(), ysfx_section_gfx
))
686 info
.hints
|= CB::PLUGIN_HAS_CUSTOM_UI
;
689 info
.name
= name
.buffer();
690 info
.label
= label
.buffer();
691 info
.maker
= maker
.buffer();
692 info
.copyright
= gCachedPluginsNullCharPtr
;
698 // -------------------------------------------------------------------------------------------------------------------
700 uint
carla_get_cached_plugin_count(CB::PluginType ptype
, const char* pluginPath
)
702 CARLA_SAFE_ASSERT_RETURN(isCachedPluginType(ptype
), 0);
703 carla_debug("carla_get_cached_plugin_count(%i:%s, %s)", ptype
, CB::PluginType2Str(ptype
), pluginPath
);
707 case CB::PLUGIN_INTERNAL
: {
709 carla_get_native_plugins_data(&count
);
713 case CB::PLUGIN_LV2
: {
714 Lv2WorldClass
& lv2World(Lv2WorldClass::getInstance());
715 lv2World
.initIfNeeded(pluginPath
);
716 return lv2World
.getPluginCount();
721 findSFZs(pluginPath
);
722 return static_cast<uint
>(gSFZs
.size());
726 case CB::PLUGIN_JSFX
:
727 findJSFXs(pluginPath
);
728 return static_cast<uint
>(gJSFXs
.size());
736 const CarlaCachedPluginInfo
* carla_get_cached_plugin_info(CB::PluginType ptype
, uint index
)
738 carla_debug("carla_get_cached_plugin_info(%i:%s, %i)", ptype
, CB::PluginType2Str(ptype
), index
);
742 case CB::PLUGIN_INTERNAL
: {
744 const NativePluginDescriptor
* const descs(carla_get_native_plugins_data(&count
));
745 CARLA_SAFE_ASSERT_BREAK(index
< count
);
746 CARLA_SAFE_ASSERT_BREAK(descs
!= nullptr);
748 const NativePluginDescriptor
& desc(descs
[index
]);
749 return get_cached_plugin_internal(desc
);
752 case CB::PLUGIN_LV2
: {
753 Lv2WorldClass
& lv2World(Lv2WorldClass::getInstance());
755 const LilvPlugin
* const cPlugin(lv2World
.getPluginFromIndex(index
));
756 CARLA_SAFE_ASSERT_BREAK(cPlugin
!= nullptr);
758 Lilv::Plugin
lilvPlugin(cPlugin
);
759 CARLA_SAFE_ASSERT_BREAK(lilvPlugin
.get_uri().is_uri());
761 return get_cached_plugin_lv2(lv2World
, lilvPlugin
);
766 CARLA_SAFE_ASSERT_BREAK(index
< gSFZs
.size());
767 return get_cached_plugin_sfz(gSFZs
[index
]);
771 case CB::PLUGIN_JSFX
:
772 CARLA_SAFE_ASSERT_BREAK(index
< static_cast<uint
>(gJSFXs
.size()));
773 return get_cached_plugin_jsfx(gJSFXs
[index
]);
780 static CarlaCachedPluginInfo info
;
784 // -------------------------------------------------------------------------------------------------------------------
786 #ifndef CARLA_PLUGIN_BUILD
787 # include "../native-plugins/_data.cpp"
790 // -------------------------------------------------------------------------------------------------------------------