Browse Source

Move some stuff from host to utils lib; Allows cached plugins in carla-plugin

tags/1.9.6
falkTX 10 years ago
parent
commit
649b5c2923
16 changed files with 1075 additions and 1034 deletions
  1. +2
    -110
      source/backend/CarlaHost.h
  2. +1
    -612
      source/backend/CarlaHostCommon.cpp
  3. +0
    -3
      source/backend/CarlaPlugin.hpp
  4. +780
    -17
      source/backend/CarlaUtils.cpp
  5. +130
    -8
      source/backend/CarlaUtils.h
  6. +10
    -5
      source/backend/Makefile
  7. +1
    -9
      source/backend/engine/CarlaEngineNative.cpp
  8. +20
    -28
      source/backend/plugin/CarlaPluginNative.cpp
  9. +2
    -197
      source/carla_backend.py
  10. +6
    -6
      source/carla_database.py
  11. +2
    -2
      source/carla_host.py
  12. +98
    -15
      source/carla_utils.py
  13. +3
    -7
      source/carla_widgets.py
  14. +10
    -2
      source/plugin/carla-base.cpp
  15. +0
    -1
      source/utils/CarlaBackendUtils.hpp
  16. +10
    -12
      source/widgets/racklistwidget.py

+ 2
- 110
source/backend/CarlaHost.h View File

@@ -142,82 +142,6 @@ typedef struct _CarlaPluginInfo {

} CarlaPluginInfo;

/*!
* Information about a cached plugin.
* @see carla_get_cached_plugin_info()
*/
typedef struct _CarlaCachedPluginInfo {
/*!
* Plugin category.
*/
PluginCategory category;

/*!
* Plugin hints.
* @see PluginHints
*/
uint hints;

/*!
* Number of audio inputs.
*/
uint32_t audioIns;

/*!
* Number of audio outputs.
*/
uint32_t audioOuts;

/*!
* Number of MIDI inputs.
*/
uint32_t midiIns;

/*!
* Number of MIDI outputs.
*/
uint32_t midiOuts;

/*!
* Number of input parameters.
*/
uint32_t parameterIns;

/*!
* Number of output parameters.
*/
uint32_t parameterOuts;

/*!
* Plugin name.
*/
const char* name;

/*!
* Plugin label.
*/
const char* label;

/*!
* Plugin author/maker.
*/
const char* maker;

/*!
* Plugin copyright/license.
*/
const char* copyright;

#ifdef __cplusplus
/*!
* C++ constructor.
*/
CARLA_API _CarlaCachedPluginInfo() noexcept;
CARLA_DECLARE_NON_COPY_STRUCT(_CarlaCachedPluginInfo)
#endif

} CarlaCachedPluginInfo;

/*!
* Port count information, used for Audio and MIDI ports and parameters.
* @see carla_get_audio_port_count_info()
@@ -347,26 +271,6 @@ typedef struct _CarlaTransportInfo {
/* ------------------------------------------------------------------------------------------------------------
* Carla Host API (C functions) */

/*!
* Get the complete license text of used third-party code and features.
* Returned string is in basic html format.
*/
CARLA_EXPORT const char* carla_get_complete_license_text();

/*!
* Get the juce version used in the current Carla build.
*/
CARLA_EXPORT const char* carla_get_juce_version();

/*!
* Get all the supported file extensions in carla_load_file().
* Returned string uses this syntax:
* @code
* "*.ext1;*.ext2;*.ext3"
* @endcode
*/
CARLA_EXPORT const char* carla_get_supported_file_extensions();

/*!
* Get how many engine drivers are available.
*/
@@ -391,18 +295,6 @@ CARLA_EXPORT const char* const* carla_get_engine_driver_device_names(uint index)
*/
CARLA_EXPORT const EngineDriverDeviceInfo* carla_get_engine_driver_device_info(uint index, const char* name);

/*!
* Get how many cached plugins are available.
* Internal, LV2 and AU plugin formats are cached and need to be discovered via this function.
* Do not call this for any other plugin formats.
*/
CARLA_EXPORT uint carla_get_cached_plugin_count(PluginType ptype, const char* pluginPath);

/*!
* Get information about a cached plugin.
*/
CARLA_EXPORT const CarlaCachedPluginInfo* carla_get_cached_plugin_info(PluginType ptype, uint index);

#ifdef __cplusplus
/*!
* Get the currently used engine, maybe be NULL.
@@ -995,12 +887,12 @@ CARLA_EXPORT const char* carla_get_host_osc_url_tcp();
CARLA_EXPORT const char* carla_get_host_osc_url_udp();

/*!
* Get the current carla library filename.
* Get the absolute filename of this carla library.
*/
CARLA_EXPORT const char* carla_get_library_filename();

/*!
* Get the folder where the current use carla library resides.
* Get the folder where this carla library resides.
*/
CARLA_EXPORT const char* carla_get_library_folder();



+ 1
- 612
source/backend/CarlaHostCommon.cpp View File

@@ -16,19 +16,8 @@
*/

#include "CarlaHost.h"
#include "CarlaNative.h"

#include "CarlaLv2Utils.hpp"
#include "CarlaPlugin.hpp"
#include "CarlaString.hpp"

#include "juce_audio_formats.h"

#ifdef CARLA_OS_MAC
# include "juce_audio_processors.h"
using juce::AudioUnitPluginFormat;
using juce::StringArray;
#endif
#include "juce_core.h"

namespace CB = CarlaBackend;

@@ -72,20 +61,6 @@ _CarlaPluginInfo::~_CarlaPluginInfo() noexcept
delete[] copyright;
}

_CarlaCachedPluginInfo::_CarlaCachedPluginInfo() noexcept
: category(CB::PLUGIN_CATEGORY_NONE),
hints(0x0),
audioIns(0),
audioOuts(0),
midiIns(0),
midiOuts(0),
parameterIns(0),
parameterOuts(0),
name(gNullCharPtr),
label(gNullCharPtr),
maker(gNullCharPtr),
copyright(gNullCharPtr) {}

_CarlaParameterInfo::_CarlaParameterInfo() noexcept
: name(gNullCharPtr),
symbol(gNullCharPtr),
@@ -122,592 +97,6 @@ _CarlaTransportInfo::_CarlaTransportInfo() noexcept

// -------------------------------------------------------------------------------------------------------------------

const char* carla_get_complete_license_text()
{
carla_debug("carla_get_complete_license_text()");

static CarlaString retText;

if (retText.isEmpty())
{
retText =
"<p>This current Carla build is using the following features and 3rd-party code:</p>"
"<ul>"

// Plugin formats
"<li>LADSPA plugin support</li>"
"<li>DSSI plugin support</li>"
"<li>LV2 plugin support</li>"
#ifdef VESTIGE_HEADER
"<li>VST2 plugin support using VeSTige header by Javier Serrano Polo</li>"
#else
"<li>VST2 plugin support using official VST SDK 2.4 [1]</li>"
#endif
#if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)
"<li>VST3 plugin support using official VST SDK 3.6 [1]</li>"
#endif
#ifdef CARLA_OS_MAC
"<li>AU plugin support</li>"
#endif

// Sample kit libraries
#ifdef HAVE_FLUIDSYNTH
"<li>FluidSynth library for SF2 support</li>"
#endif
#ifdef HAVE_LINUXSAMPLER
"<li>LinuxSampler library for GIG and SFZ support [2]</li>"
#endif

// Internal plugins
"<li>NekoFilter plugin code based on lv2fil by Nedko Arnaudov and Fons Adriaensen</li>"
#ifdef WANT_ZYNADDSUBFX
"<li>ZynAddSubFX plugin code</li>"
#endif

// misc libs
"<li>base64 utilities based on code by Ren\u00E9 Nyffenegger</li>"
#ifdef CARLA_OS_MAC
"<li>sem_timedwait for Mac OS by Keith Shortridge</li>"
#endif
"<li>liblo library for OSC support</li>"
"<li>rtmempool library by Nedko Arnaudov"
"<li>serd, sord, sratom and lilv libraries for LV2 discovery</li>"
#if ! (defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN))
"<li>RtAudio and RtMidi libraries for extra Audio and MIDI support</li>"
#endif

// end
"</ul>"

"<p>"
#if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN) || ! defined(VESTIGE_HEADER)
// Required by VST SDK
"&nbsp;[1] Trademark of Steinberg Media Technologies GmbH.<br/>"
#endif
#ifdef HAVE_LINUXSAMPLER
// LinuxSampler GPL exception
"&nbsp;[2] Using LinuxSampler code in commercial hardware or software products is not allowed without prior written authorization by the authors."
#endif
"</p>"
;
}

return retText;
}

const char* carla_get_juce_version()
{
carla_debug("carla_get_juce_version()");

static CarlaString retVersion;

if (retVersion.isEmpty())
{
if (const char* const version = juce::SystemStats::getJUCEVersion().toRawUTF8())
retVersion = version+6;
else
retVersion = "3.0";
}

return retVersion;
}

const char* carla_get_supported_file_extensions()
{
carla_debug("carla_get_supported_file_extensions()");

static CarlaString retText;

if (retText.isEmpty())
{
retText =
// Base types
"*.carxp;*.carxs"
// MIDI files
";*.mid;*.midi"
#ifdef HAVE_FLUIDSYNTH
// fluidsynth (sf2)
";*.sf2"
#endif
#ifdef HAVE_LINUXSAMPLER
// linuxsampler (gig and sfz)
";*.gig;*.sfz"
#endif
#ifdef WANT_ZYNADDSUBFX
// zynaddsubfx presets
";*.xmz;*.xiz"
#endif
;

#ifndef BUILD_BRIDGE
// Audio files
{
using namespace juce;

AudioFormatManager afm;
afm.registerBasicFormats();

String juceFormats;

for (AudioFormat **it=afm.begin(), **end=afm.end(); it != end; ++it)
{
const StringArray& exts((*it)->getFileExtensions());

for (String *eit=exts.begin(), *eend=exts.end(); eit != eend; ++eit)
juceFormats += String(";*" + (*eit)).toRawUTF8();
}

retText += juceFormats.toRawUTF8();
}
#endif
}

return retText;
}

// -------------------------------------------------------------------------------------------------------------------

#ifdef CARLA_OS_MAC
static StringArray gCachedAuPluginResults;
#endif

uint carla_get_cached_plugin_count(PluginType ptype, const char* pluginPath)
{
CARLA_SAFE_ASSERT_RETURN(ptype == CB::PLUGIN_INTERNAL || ptype == CB::PLUGIN_LV2 || ptype == CB::PLUGIN_AU, 0);
carla_debug("carla_get_cached_plugin_count(%i:%s)", ptype, CB::PluginType2Str(ptype));

switch (ptype)
{
case CB::PLUGIN_INTERNAL: {
#ifndef BUILD_BRIDGE
return static_cast<uint>(CarlaPlugin::getNativePluginCount());
#else
return 0;
#endif
}

case CB::PLUGIN_LV2: {
Lv2WorldClass& lv2World(Lv2WorldClass::getInstance());
lv2World.initIfNeeded(pluginPath);
return lv2World.getPluginCount();
}

case CB::PLUGIN_AU: {
#ifdef CARLA_OS_MAC
static bool initiated = false;

if (initiated)
return static_cast<uint>(gCachedAuPluginResults.size());

initiated = true;
AudioUnitPluginFormat auFormat;
gCachedAuPluginResults = auFormat.searchPathsForPlugins(juce::FileSearchPath(), false);

return static_cast<uint>(gCachedAuPluginResults.size());
#else
return 0;
#endif
}

default:
return 0;
}
}

const CarlaCachedPluginInfo* carla_get_cached_plugin_info(PluginType ptype, uint index)
{
carla_debug("carla_get_cached_plugin_info(%i:%s, %i)", ptype, CB::PluginType2Str(ptype), index);

static CarlaCachedPluginInfo info;

switch (ptype)
{
case CB::PLUGIN_INTERNAL: {
#ifndef BUILD_BRIDGE
const NativePluginDescriptor* const desc(CarlaPlugin::getNativePluginDescriptor(index));
CARLA_SAFE_ASSERT_BREAK(desc != nullptr);

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_SINGLE_THREAD)
info.hints |= CB::PLUGIN_NEEDS_SINGLE_THREAD;

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;
#else
break;
#endif
}

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());

carla_stdout("Filling info for LV2 with URI '%s'", lilvPlugin.get_uri().as_uri());

// features
info.hints = 0x0;

if (lilvPlugin.get_uis().size() > 0 || lilvPlugin.get_modgui_resources_directory().as_uri() != nullptr)
info.hints |= CB::PLUGIN_HAS_CUSTOM_UI;

{
Lilv::Nodes lilvFeatureNodes(lilvPlugin.get_supported_features());

LILV_FOREACH(nodes, it, lilvFeatureNodes)
{
Lilv::Node lilvFeatureNode(lilvFeatureNodes.get(it));
const char* const featureURI(lilvFeatureNode.as_uri());
CARLA_SAFE_ASSERT_CONTINUE(featureURI != nullptr);

if (std::strcmp(featureURI, LV2_CORE__hardRTCapable) == 0)
info.hints |= CB::PLUGIN_IS_RTSAFE;
}

lilv_nodes_free(const_cast<LilvNodes*>(lilvFeatureNodes.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;
}
}

lilv_nodes_free(const_cast<LilvNodes*>(typeNodes.me));
}

// number data
info.audioIns = 0;
info.audioOuts = 0;
info.midiIns = 0;
info.midiOuts = 0;
info.parameterIns = 0;
info.parameterOuts = 0;

for (uint i=0, count=lilvPlugin.get_num_ports(); i<count; ++i)
{
Lilv::Port lilvPort(lilvPlugin.get_port_by_index(i));

bool isInput;

/**/ if (lilvPort.is_a(lv2World.port_input))
isInput = true;
else if (lilvPort.is_a(lv2World.port_output))
isInput = false;
else
continue;

/**/ if (lilvPort.is_a(lv2World.port_control))
{
// skip some control ports
if (lilvPort.has_property(lv2World.reportsLatency))
continue;

if (LilvNode* const designationNode = lilv_port_get(lilvPort.parent, lilvPort.me, lv2World.designation.me))
{
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 (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));

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

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);
else
++(info.midiOuts);
}
}
else if (lilvPort.is_a(lv2World.port_midi))
{
if (isInput)
++(info.midiIns);
else
++(info.midiOuts);
}
}

// text data
static CarlaString suri, sname, smaker, slicense;
suri.clear(); sname.clear(); smaker.clear(); slicense.clear();

suri = lilvPlugin.get_uri().as_uri();

if (const char* const name = lilvPlugin.get_name().as_string())
sname = name;
else
sname.clear();

if (const char* const author = lilvPlugin.get_author_name().as_string())
smaker = author;
else
smaker.clear();

Lilv::Nodes licenseNodes(lilvPlugin.get_value(lv2World.doap_license));

if (licenseNodes.size() > 0)
{
if (const char* const license = licenseNodes.get_first().as_string())
slicense = license;
}

lilv_nodes_free(const_cast<LilvNodes*>(licenseNodes.me));

info.name = sname;
info.label = suri;
info.maker = smaker;
info.copyright = slicense;

return &info;
}

case CB::PLUGIN_AU: {
#ifdef CARLA_OS_MAC
const int indexi(static_cast<int>(index));
CARLA_SAFE_ASSERT_BREAK(indexi < gCachedAuPluginResults.size());

using namespace juce;

String pluginId(gCachedAuPluginResults[indexi]);
OwnedArray<PluginDescription> results;

AudioUnitPluginFormat auFormat;
auFormat.findAllTypesForFile(results, pluginId);
CARLA_SAFE_ASSERT_BREAK(results.size() > 0);
CARLA_SAFE_ASSERT(results.size() == 1);

PluginDescription* const desc(results[0]);
CARLA_SAFE_ASSERT_BREAK(desc != nullptr);

info.category = CB::getPluginCategoryFromName(desc->category.toRawUTF8());
info.hints = 0x0;

if (desc->isInstrument)
info.hints |= CB::PLUGIN_IS_SYNTH;
if (true)
info.hints |= CB::PLUGIN_HAS_CUSTOM_UI;

info.audioIns = static_cast<uint32_t>(desc->numInputChannels);
info.audioOuts = static_cast<uint32_t>(desc->numOutputChannels);
info.midiIns = desc->isInstrument ? 1 : 0;
info.midiOuts = 0;
info.parameterIns = 0;
info.parameterOuts = 0;

static CarlaString sname, slabel, smaker;

sname = desc->name.toRawUTF8();
slabel = desc->fileOrIdentifier.toRawUTF8();
smaker = desc->manufacturerName.toRawUTF8();

info.name = sname;
info.label = slabel;
info.maker = smaker;
info.copyright = gNullCharPtr;

return &info;
#else
break;
#endif
}

default:
break;
}

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;
return &info;
}

// -------------------------------------------------------------------------------------------------------------------

const char* carla_get_library_filename()
{
carla_debug("carla_get_library_filename()");


+ 0
- 3
source/backend/CarlaPlugin.hpp View File

@@ -889,9 +889,6 @@ public:
const int64_t uniqueId;
};

static std::size_t getNativePluginCount() noexcept;
static const NativePluginDescriptor* getNativePluginDescriptor(const std::size_t index) noexcept;

static CarlaPlugin* newNative(const Initializer& init);
static CarlaPlugin* newBridge(const Initializer& init, const BinaryType btype, const PluginType ptype, const char* const bridgeBinary);



+ 780
- 17
source/backend/CarlaUtils.cpp View File

@@ -17,48 +17,779 @@

#include "CarlaUtils.h"

#include "CarlaNative.h"

#include "CarlaBackendUtils.hpp"
#include "CarlaLv2Utils.hpp"
#include "CarlaPipeUtils.hpp"
#include "CarlaThread.hpp"
#include "LinkedList.hpp"

#include "juce_audio_formats.h"

#ifdef CARLA_OS_MAC
# include "juce_audio_processors.h"
#endif

#include "juce_core.h"
namespace CB = CarlaBackend;

static const char* const gNullCharPtr = "";

#ifdef CARLA_OS_MAC
static juce::StringArray gCachedAuPluginResults;
#endif

// -------------------------------------------------------------------------------------------------------------------

const char* carla_get_library_filename()
_CarlaCachedPluginInfo::_CarlaCachedPluginInfo() noexcept
: category(CB::PLUGIN_CATEGORY_NONE),
hints(0x0),
audioIns(0),
audioOuts(0),
midiIns(0),
midiOuts(0),
parameterIns(0),
parameterOuts(0),
name(gNullCharPtr),
label(gNullCharPtr),
maker(gNullCharPtr),
copyright(gNullCharPtr) {}

// -------------------------------------------------------------------------------------------------------------------

static const NativePluginDescriptor carlaRackDesc = {
/* category */ NATIVE_PLUGIN_CATEGORY_OTHER,
/* hints */ static_cast<NativePluginHints>(NATIVE_PLUGIN_IS_SYNTH
|NATIVE_PLUGIN_HAS_UI
|NATIVE_PLUGIN_NEEDS_FIXED_BUFFERS
|NATIVE_PLUGIN_NEEDS_SINGLE_THREAD
|NATIVE_PLUGIN_USES_STATE
|NATIVE_PLUGIN_USES_TIME),
/* supports */ static_cast<NativePluginSupports>(NATIVE_PLUGIN_SUPPORTS_EVERYTHING),
/* audioIns */ 2,
/* audioOuts */ 2,
/* midiIns */ 1,
/* midiOuts */ 1,
/* paramIns */ 0,
/* paramOuts */ 0,
/* name */ "Carla-Rack",
/* label */ "carlarack",
/* maker */ "falkTX",
/* copyright */ "GNU GPL v2+",
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr,
};

static const NativePluginDescriptor carlaPatchbayDesc = {
/* category */ NATIVE_PLUGIN_CATEGORY_OTHER,
/* hints */ static_cast<NativePluginHints>(NATIVE_PLUGIN_IS_SYNTH
|NATIVE_PLUGIN_HAS_UI
|NATIVE_PLUGIN_NEEDS_FIXED_BUFFERS
|NATIVE_PLUGIN_NEEDS_SINGLE_THREAD
|NATIVE_PLUGIN_USES_STATE
|NATIVE_PLUGIN_USES_TIME),
/* supports */ static_cast<NativePluginSupports>(NATIVE_PLUGIN_SUPPORTS_EVERYTHING),
/* audioIns */ 2,
/* audioOuts */ 2,
/* midiIns */ 1,
/* midiOuts */ 1,
/* paramIns */ 0,
/* paramOuts */ 0,
/* name */ "Carla-Patchbay",
/* label */ "carlapatchbay",
/* maker */ "falkTX",
/* copyright */ "GNU GPL v2+",
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr,
};

static const NativePluginDescriptor carlaPatchbay3sDesc = {
/* category */ NATIVE_PLUGIN_CATEGORY_OTHER,
/* hints */ static_cast<NativePluginHints>(NATIVE_PLUGIN_IS_SYNTH
|NATIVE_PLUGIN_HAS_UI
|NATIVE_PLUGIN_NEEDS_FIXED_BUFFERS
|NATIVE_PLUGIN_NEEDS_SINGLE_THREAD
|NATIVE_PLUGIN_USES_STATE
|NATIVE_PLUGIN_USES_TIME),
/* supports */ static_cast<NativePluginSupports>(NATIVE_PLUGIN_SUPPORTS_EVERYTHING),
/* audioIns */ 3,
/* audioOuts */ 2,
/* midiIns */ 1,
/* midiOuts */ 1,
/* paramIns */ 0,
/* paramOuts */ 0,
/* name */ "Carla-Patchbay (sidechain)",
/* label */ "carlapatchbay3s",
/* maker */ "falkTX",
/* copyright */ "GNU GPL v2+",
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr,
};

static const NativePluginDescriptor carlaPatchbay16Desc = {
/* category */ NATIVE_PLUGIN_CATEGORY_OTHER,
/* hints */ static_cast<NativePluginHints>(NATIVE_PLUGIN_IS_SYNTH
|NATIVE_PLUGIN_HAS_UI
|NATIVE_PLUGIN_NEEDS_FIXED_BUFFERS
|NATIVE_PLUGIN_NEEDS_SINGLE_THREAD
|NATIVE_PLUGIN_USES_STATE
|NATIVE_PLUGIN_USES_TIME),
/* supports */ static_cast<NativePluginSupports>(NATIVE_PLUGIN_SUPPORTS_EVERYTHING),
/* audioIns */ 16,
/* audioOuts */ 16,
/* midiIns */ 1,
/* midiOuts */ 1,
/* paramIns */ 0,
/* paramOuts */ 0,
/* name */ "Carla-Patchbay (16chan)",
/* label */ "carlapatchbay16",
/* maker */ "falkTX",
/* copyright */ "GNU GPL v2+",
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr,
};

static const NativePluginDescriptor carlaPatchbay32Desc = {
/* category */ NATIVE_PLUGIN_CATEGORY_OTHER,
/* hints */ static_cast<NativePluginHints>(NATIVE_PLUGIN_IS_SYNTH
|NATIVE_PLUGIN_HAS_UI
|NATIVE_PLUGIN_NEEDS_FIXED_BUFFERS
|NATIVE_PLUGIN_NEEDS_SINGLE_THREAD
|NATIVE_PLUGIN_USES_STATE
|NATIVE_PLUGIN_USES_TIME),
/* supports */ static_cast<NativePluginSupports>(NATIVE_PLUGIN_SUPPORTS_EVERYTHING),
/* audioIns */ 32,
/* audioOuts */ 32,
/* midiIns */ 1,
/* midiOuts */ 1,
/* paramIns */ 0,
/* paramOuts */ 0,
/* name */ "Carla-Patchbay (32chan)",
/* label */ "carlapatchbay32",
/* maker */ "falkTX",
/* copyright */ "GNU GPL v2+",
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr,
};

static LinkedList<const NativePluginDescriptor*> gPluginDescriptors;

static const
struct ScopedInitializer {
ScopedInitializer()
{
carla_register_all_plugins();
}

~ScopedInitializer()
{
gPluginDescriptors.clear();
}
} _si;

// -------------------------------------------------------------------------------------------------------------------

CARLA_EXTERN_C
void carla_register_native_plugin_carla();

void carla_register_native_plugin_carla()
{
carla_debug("carla_get_library_filename()");
gPluginDescriptors.append(&carlaRackDesc);
gPluginDescriptors.append(&carlaPatchbayDesc);
gPluginDescriptors.append(&carlaPatchbay3sDesc);
gPluginDescriptors.append(&carlaPatchbay16Desc);
gPluginDescriptors.append(&carlaPatchbay32Desc);
}

static CarlaString ret;
void carla_register_native_plugin(const NativePluginDescriptor* desc)
{
gPluginDescriptors.append(desc);
}

if (ret.isEmpty())
// -------------------------------------------------------------------------------------------------------------------

const char* carla_get_complete_license_text()
{
carla_debug("carla_get_complete_license_text()");

static CarlaString retText;

if (retText.isEmpty())
{
using juce::File;
ret = File(File::getSpecialLocation(File::currentExecutableFile)).getFullPathName().toRawUTF8();
retText =
"<p>This current Carla build is using the following features and 3rd-party code:</p>"
"<ul>"

// Plugin formats
"<li>LADSPA plugin support</li>"
"<li>DSSI plugin support</li>"
"<li>LV2 plugin support</li>"
#ifdef VESTIGE_HEADER
"<li>VST2 plugin support using VeSTige header by Javier Serrano Polo</li>"
#else
"<li>VST2 plugin support using official VST SDK 2.4 [1]</li>"
#endif
#if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)
"<li>VST3 plugin support using official VST SDK 3.6 [1]</li>"
#endif
#ifdef CARLA_OS_MAC
"<li>AU plugin support</li>"
#endif

// Sample kit libraries
#ifdef HAVE_FLUIDSYNTH
"<li>FluidSynth library for SF2 support</li>"
#endif
#ifdef HAVE_LINUXSAMPLER
"<li>LinuxSampler library for GIG and SFZ support [2]</li>"
#endif

// Internal plugins
"<li>NekoFilter plugin code based on lv2fil by Nedko Arnaudov and Fons Adriaensen</li>"
#ifdef WANT_ZYNADDSUBFX
"<li>ZynAddSubFX plugin code</li>"
#endif

// misc libs
"<li>base64 utilities based on code by Ren\u00E9 Nyffenegger</li>"
#ifdef CARLA_OS_MAC
"<li>sem_timedwait for Mac OS by Keith Shortridge</li>"
#endif
"<li>liblo library for OSC support</li>"
"<li>rtmempool library by Nedko Arnaudov"
"<li>serd, sord, sratom and lilv libraries for LV2 discovery</li>"
#if ! (defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN))
"<li>RtAudio and RtMidi libraries for extra Audio and MIDI support</li>"
#endif

// end
"</ul>"

"<p>"
#if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN) || ! defined(VESTIGE_HEADER)
// Required by VST SDK
"&nbsp;[1] Trademark of Steinberg Media Technologies GmbH.<br/>"
#endif
#ifdef HAVE_LINUXSAMPLER
// LinuxSampler GPL exception
"&nbsp;[2] Using LinuxSampler code in commercial hardware or software products is not allowed without prior written authorization by the authors."
#endif
"</p>"
;
}

return ret;
return retText;
}

const char* carla_get_library_folder()
const char* carla_get_juce_version()
{
carla_debug("carla_get_library_folder()");
carla_debug("carla_get_juce_version()");

static CarlaString ret;
static CarlaString retVersion;

if (ret.isEmpty())
if (retVersion.isEmpty())
{
using juce::File;
ret = File(File::getSpecialLocation(File::currentExecutableFile).getParentDirectory()).getFullPathName().toRawUTF8();
if (const char* const version = juce::SystemStats::getJUCEVersion().toRawUTF8())
retVersion = version+6;
else
retVersion = "3.0";
}

return ret;
return retVersion;
}

const char* carla_get_supported_file_extensions()
{
carla_debug("carla_get_supported_file_extensions()");

static CarlaString retText;

if (retText.isEmpty())
{
retText =
// Base types
"*.carxp;*.carxs"
// MIDI files
";*.mid;*.midi"
#ifdef HAVE_FLUIDSYNTH
// fluidsynth (sf2)
";*.sf2"
#endif
#ifdef HAVE_LINUXSAMPLER
// linuxsampler (gig and sfz)
";*.gig;*.sfz"
#endif
#ifdef WANT_ZYNADDSUBFX
// zynaddsubfx presets
";*.xmz;*.xiz"
#endif
;

// Audio files
{
using namespace juce;

AudioFormatManager afm;
afm.registerBasicFormats();

String juceFormats;

for (AudioFormat **it=afm.begin(), **end=afm.end(); it != end; ++it)
{
const StringArray& exts((*it)->getFileExtensions());

for (String *eit=exts.begin(), *eend=exts.end(); eit != eend; ++eit)
juceFormats += String(";*" + (*eit)).toRawUTF8();
}

retText += juceFormats.toRawUTF8();
}
}

return retText;
}

// -------------------------------------------------------------------------------------------------------------------

void carla_set_locale_C()
uint carla_get_cached_plugin_count(CB::PluginType ptype, const char* pluginPath)
{
::setlocale(LC_NUMERIC, "C");
CARLA_SAFE_ASSERT_RETURN(ptype == CB::PLUGIN_INTERNAL || ptype == CB::PLUGIN_LV2 || ptype == CB::PLUGIN_AU, 0);
carla_debug("carla_get_cached_plugin_count(%i:%s)", ptype, CB::PluginType2Str(ptype));

switch (ptype)
{
case CB::PLUGIN_INTERNAL: {
return static_cast<uint>(gPluginDescriptors.count());
}

case CB::PLUGIN_LV2: {
Lv2WorldClass& lv2World(Lv2WorldClass::getInstance());
lv2World.initIfNeeded(pluginPath);
return lv2World.getPluginCount();
}

case CB::PLUGIN_AU: {
#ifdef CARLA_OS_MAC
static bool initiated = false;

if (initiated)
return static_cast<uint>(gCachedAuPluginResults.size());

using namespace juce;

initiated = true;
AudioUnitPluginFormat auFormat;
gCachedAuPluginResults = auFormat.searchPathsForPlugins(juce::FileSearchPath(), false);

return static_cast<uint>(gCachedAuPluginResults.size());
#else
return 0;
#endif
}

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

static CarlaCachedPluginInfo info;

switch (ptype)
{
case CB::PLUGIN_INTERNAL: {
const NativePluginDescriptor* const desc(gPluginDescriptors.getAt(index, nullptr));
CARLA_SAFE_ASSERT_BREAK(desc != nullptr);

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_SINGLE_THREAD)
info.hints |= CB::PLUGIN_NEEDS_SINGLE_THREAD;

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

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());

carla_stdout("Filling info for LV2 with URI '%s'", lilvPlugin.get_uri().as_uri());

// features
info.hints = 0x0;

#if 0
if (lilvPlugin.get_uis().size() > 0 || lilvPlugin.get_modgui_resources_directory().as_uri() != nullptr)
info.hints |= CB::PLUGIN_HAS_CUSTOM_UI;
#endif

{
Lilv::Nodes lilvFeatureNodes(lilvPlugin.get_supported_features());

LILV_FOREACH(nodes, it, lilvFeatureNodes)
{
Lilv::Node lilvFeatureNode(lilvFeatureNodes.get(it));
const char* const featureURI(lilvFeatureNode.as_uri());
CARLA_SAFE_ASSERT_CONTINUE(featureURI != nullptr);

if (std::strcmp(featureURI, LV2_CORE__hardRTCapable) == 0)
info.hints |= CB::PLUGIN_IS_RTSAFE;
}

lilv_nodes_free(const_cast<LilvNodes*>(lilvFeatureNodes.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;
}
}

lilv_nodes_free(const_cast<LilvNodes*>(typeNodes.me));
}

// number data
info.audioIns = 0;
info.audioOuts = 0;
info.midiIns = 0;
info.midiOuts = 0;
info.parameterIns = 0;
info.parameterOuts = 0;

for (uint i=0, count=lilvPlugin.get_num_ports(); i<count; ++i)
{
Lilv::Port lilvPort(lilvPlugin.get_port_by_index(i));

bool isInput;

/**/ if (lilvPort.is_a(lv2World.port_input))
isInput = true;
else if (lilvPort.is_a(lv2World.port_output))
isInput = false;
else
continue;

/**/ if (lilvPort.is_a(lv2World.port_control))
{
// skip some control ports
if (lilvPort.has_property(lv2World.reportsLatency))
continue;

if (LilvNode* const designationNode = lilv_port_get(lilvPort.parent, lilvPort.me, lv2World.designation.me))
{
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 (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));

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

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);
else
++(info.midiOuts);
}
}
else if (lilvPort.is_a(lv2World.port_midi))
{
if (isInput)
++(info.midiIns);
else
++(info.midiOuts);
}
}

// text data
static CarlaString suri, sname, smaker, slicense;
suri.clear(); sname.clear(); smaker.clear(); slicense.clear();

suri = lilvPlugin.get_uri().as_uri();

if (const char* const name = lilvPlugin.get_name().as_string())
sname = name;
else
sname.clear();

if (const char* const author = lilvPlugin.get_author_name().as_string())
smaker = author;
else
smaker.clear();

Lilv::Nodes licenseNodes(lilvPlugin.get_value(lv2World.doap_license));

if (licenseNodes.size() > 0)
{
if (const char* const license = licenseNodes.get_first().as_string())
slicense = license;
}

lilv_nodes_free(const_cast<LilvNodes*>(licenseNodes.me));

info.name = sname;
info.label = suri;
info.maker = smaker;
info.copyright = slicense;

return &info;
}

case CB::PLUGIN_AU: {
#ifdef CARLA_OS_MAC
const int indexi(static_cast<int>(index));
CARLA_SAFE_ASSERT_BREAK(indexi < gCachedAuPluginResults.size());

using namespace juce;

String pluginId(gCachedAuPluginResults[indexi]);
OwnedArray<PluginDescription> results;

AudioUnitPluginFormat auFormat;
auFormat.findAllTypesForFile(results, pluginId);
CARLA_SAFE_ASSERT_BREAK(results.size() > 0);
CARLA_SAFE_ASSERT(results.size() == 1);

PluginDescription* const desc(results[0]);
CARLA_SAFE_ASSERT_BREAK(desc != nullptr);

info.category = CB::getPluginCategoryFromName(desc->category.toRawUTF8());
info.hints = 0x0;

if (desc->isInstrument)
info.hints |= CB::PLUGIN_IS_SYNTH;
if (true)
info.hints |= CB::PLUGIN_HAS_CUSTOM_UI;

info.audioIns = static_cast<uint32_t>(desc->numInputChannels);
info.audioOuts = static_cast<uint32_t>(desc->numOutputChannels);
info.midiIns = desc->isInstrument ? 1 : 0;
info.midiOuts = 0;
info.parameterIns = 0;
info.parameterOuts = 0;

static CarlaString sname, slabel, smaker;

sname = desc->name.toRawUTF8();
slabel = desc->fileOrIdentifier.toRawUTF8();
smaker = desc->manufacturerName.toRawUTF8();

info.name = sname;
info.label = slabel;
info.maker = smaker;
info.copyright = gNullCharPtr;

return &info;
#else
break;
#endif
}

default:
break;
}

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;
return &info;
}

// -------------------------------------------------------------------------------------------------------------------
@@ -202,6 +933,38 @@ void carla_pipe_client_destroy(CarlaPipeClientHandle handle)

// -------------------------------------------------------------------------------------------------------------------

const char* carla_get_library_filename()
{
carla_debug("carla_get_library_filename()");

static CarlaString ret;

if (ret.isEmpty())
{
using juce::File;
ret = File(File::getSpecialLocation(File::currentExecutableFile)).getFullPathName().toRawUTF8();
}

return ret;
}

const char* carla_get_library_folder()
{
carla_debug("carla_get_library_folder()");

static CarlaString ret;

if (ret.isEmpty())
{
using juce::File;
ret = File(File::getSpecialLocation(File::currentExecutableFile).getParentDirectory()).getFullPathName().toRawUTF8();
}

return ret;
}

// -------------------------------------------------------------------------------------------------------------------

#include "CarlaPipeUtils.cpp"

// -------------------------------------------------------------------------------------------------------------------

+ 130
- 8
source/backend/CarlaUtils.h View File

@@ -18,7 +18,12 @@
#ifndef CARLA_UTILS_H_INCLUDED
#define CARLA_UTILS_H_INCLUDED

#include "CarlaDefines.h"
#include "CarlaBackend.h"

#ifdef __cplusplus
using CarlaBackend::PluginCategory;
using CarlaBackend::PluginType;
#endif

/*!
* @defgroup CarlaUtilsAPI Carla Utils API
@@ -40,25 +45,127 @@ typedef void* CarlaPipeClientHandle;
typedef void (*CarlaPipeCallbackFunc)(void* ptr, const char* msg);

/*!
* Get the current carla library filename.
* Information about a cached plugin.
* @see carla_get_cached_plugin_info()
*/
CARLA_EXPORT const char* carla_get_library_filename();
typedef struct _CarlaCachedPluginInfo {
/*!
* Plugin category.
*/
PluginCategory category;

/*!
* Plugin hints.
* @see PluginHints
*/
uint hints;

/*!
* Number of audio inputs.
*/
uint32_t audioIns;

/*!
* Number of audio outputs.
*/
uint32_t audioOuts;

/*!
* Number of MIDI inputs.
*/
uint32_t midiIns;

/*!
* Number of MIDI outputs.
*/
uint32_t midiOuts;

/*!
* Number of input parameters.
*/
uint32_t parameterIns;

/*!
* Number of output parameters.
*/
uint32_t parameterOuts;

/*!
* Plugin name.
*/
const char* name;

/*!
* Plugin label.
*/
const char* label;

/*!
* Plugin author/maker.
*/
const char* maker;

/*!
* Plugin copyright/license.
*/
const char* copyright;

#ifdef __cplusplus
/*!
* C++ constructor.
*/
CARLA_API _CarlaCachedPluginInfo() noexcept;
CARLA_DECLARE_NON_COPY_STRUCT(_CarlaCachedPluginInfo)
#endif

} CarlaCachedPluginInfo;

/* ------------------------------------------------------------------------------------------------------------
* get stuff */

/*!
* Get the folder where the current use carla library resides.
* Get the complete license text of used third-party code and features.
* Returned string is in basic html format.
*/
CARLA_EXPORT const char* carla_get_library_folder();
CARLA_EXPORT const char* carla_get_complete_license_text();

/*!
* TODO.
* Get the juce version used in the current Carla build.
*/
CARLA_EXPORT void carla_set_locale_C();
CARLA_EXPORT const char* carla_get_juce_version();

/*!
* Get the juce version used in the current Carla build.
* Get all the supported file extensions in carla_load_file().
* Returned string uses this syntax:
* @code
* "*.ext1;*.ext2;*.ext3"
* @endcode
*/
CARLA_EXPORT const char* carla_get_supported_file_extensions();

/*!
* Get how many cached plugins are available.
* Internal, LV2 and AU plugin formats are cached and need to be discovered via this function.
* Do not call this for any other plugin formats.
*/
CARLA_EXPORT uint carla_get_cached_plugin_count(PluginType ptype, const char* pluginPath);

/*!
* Get information about a cached plugin.
*/
CARLA_EXPORT const CarlaCachedPluginInfo* carla_get_cached_plugin_info(PluginType ptype, uint index);

/* ------------------------------------------------------------------------------------------------------------
* set stuff */

/*!
* Set the current process name to @a name.
*/
CARLA_EXPORT void carla_set_process_name(const char* name);

/* ------------------------------------------------------------------------------------------------------------
* pipes */

/*!
* TODO.
*/
@@ -114,6 +221,21 @@ CARLA_EXPORT bool carla_pipe_client_flush_and_unlock(CarlaPipeClientHandle handl
*/
CARLA_EXPORT void carla_pipe_client_destroy(CarlaPipeClientHandle handle);

/* ------------------------------------------------------------------------------------------------------------
* info about current library */

/*!
* Get the absolute filename of this carla library.
*/
CARLA_EXPORT const char* carla_get_library_filename();

/*!
* Get the folder where this carla library resides.
*/
CARLA_EXPORT const char* carla_get_library_folder();

// -------------------------------------------------------------------------------------------------------------------

/** @} */

#endif /* CARLA_UTILS_H_INCLUDED */

+ 10
- 5
source/backend/Makefile View File

@@ -45,7 +45,11 @@ STANDALONE_LIBS += $(MODULEDIR)/rtaudio.a
STANDALONE_LIBS += $(MODULEDIR)/rtmidi.a
endif

UTILS_LIBS = $(MODULEDIR)/juce_core.a
UTILS_LIBS = $(MODULEDIR)/juce_audio_basics.a
UTILS_LIBS += $(MODULEDIR)/juce_audio_formats.a
UTILS_LIBS += $(MODULEDIR)/juce_core.a
UTILS_LIBS += $(MODULEDIR)/lilv.a
UTILS_LIBS += $(MODULEDIR)/native-plugins.a

# ----------------------------------------------------------------------------------------------------------------------------

@@ -89,10 +93,11 @@ ifeq ($(UNIX),true)
STANDALONE_LINK_FLAGS += -lmagic
endif

UTILS_LINK_FLAGS = $(JUCE_CORE_LIBS)
ifeq ($(UNIX),true)
UTILS_LINK_FLAGS += -lpthread
endif
UTILS_LINK_FLAGS = $(JUCE_AUDIO_BASICS_LIBS)
UTILS_LINK_FLAGS += $(JUCE_AUDIO_FORMATS_LIBS)
UTILS_LINK_FLAGS += $(JUCE_CORE_LIBS)
UTILS_LINK_FLAGS += $(LILV_LIBS)
UTILS_LINK_FLAGS += $(NATIVE_PLUGINS_LIBS)

# ----------------------------------------------------------------------------------------------------------------------------



+ 1
- 9
source/backend/engine/CarlaEngineNative.cpp View File

@@ -31,6 +31,7 @@
#include "CarlaStateUtils.hpp"

#include "CarlaExternalUI.hpp"
#include "CarlaHost.h"
#include "CarlaNative.hpp"

#include "juce_audio_basics.h"
@@ -973,15 +974,6 @@ protected:

const CarlaMutexLocker cml(fUiServer.getPipeLock());

fUiServer.writeAndFixMessage("complete-license");
fUiServer.writeAndFixMessage(carla_get_complete_license_text());

fUiServer.writeAndFixMessage("juce-version");
fUiServer.writeAndFixMessage(carla_get_juce_version());

fUiServer.writeAndFixMessage("file-exts");
fUiServer.writeAndFixMessage(carla_get_supported_file_extensions());

fUiServer.writeAndFixMessage("max-plugin-number");
std::sprintf(fTmpBuf, "%i\n", pData->maxPluginNumber);
fUiServer.writeMessage(fTmpBuf);


+ 20
- 28
source/backend/plugin/CarlaPluginNative.cpp View File

@@ -26,7 +26,15 @@
using juce::String;
using juce::StringArray;

// -----------------------------------------------------
// -----------------------------------------------------------------------

CARLA_EXTERN_C
std::size_t carla_getNativePluginCount();

CARLA_EXTERN_C
const NativePluginDescriptor* carla_getNativePluginDescriptor(const std::size_t index);

// -----------------------------------------------------------------------

static LinkedList<const NativePluginDescriptor*> gPluginDescriptors;

@@ -35,6 +43,16 @@ void carla_register_native_plugin(const NativePluginDescriptor* desc)
gPluginDescriptors.append(desc);
}

std::size_t carla_getNativePluginCount()
{
return gPluginDescriptors.count();
}

const NativePluginDescriptor* carla_getNativePluginDescriptor(const std::size_t index)
{
return gPluginDescriptors.getAt(index, nullptr);
}

// -----------------------------------------------------

CARLA_BACKEND_START_NAMESPACE
@@ -123,7 +141,7 @@ struct ScopedInitializer {
carla_register_all_plugins();
}

~ScopedInitializer()
~ScopedInitializer() noexcept
{
gPluginDescriptors.clear();
}
@@ -2201,20 +2219,6 @@ protected:
// -------------------------------------------------------------------

public:
static size_t getPluginCount() noexcept
{
return gPluginDescriptors.count();
}

static const NativePluginDescriptor* getPluginDescriptor(const size_t index) noexcept
{
CARLA_SAFE_ASSERT_RETURN(index < gPluginDescriptors.count(), nullptr);

return gPluginDescriptors.getAt(index, nullptr);
}

// -------------------------------------------------------------------

void* getNativeHandle() const noexcept override
{
return fHandle;
@@ -2435,18 +2439,6 @@ private:

// -----------------------------------------------------------------------

std::size_t CarlaPlugin::getNativePluginCount() noexcept
{
return CarlaPluginNative::getPluginCount();
}

const NativePluginDescriptor* CarlaPlugin::getNativePluginDescriptor(const std::size_t index) noexcept
{
return CarlaPluginNative::getPluginDescriptor(index);
}

// -----------------------------------------------------------------------

CarlaPlugin* CarlaPlugin::newNative(const Initializer& init)
{
carla_debug("CarlaPlugin::newNative({%p, \"%s\", \"%s\", \"%s\", " P_INT64 "})", init.engine, init.filename, init.name, init.label, init.uniqueId);


+ 2
- 197
source/carla_backend.py View File

@@ -1060,48 +1060,6 @@ class CarlaPluginInfo(Structure):
("uniqueId", c_int64)
]

# Information about an internal Carla plugin.
# @see carla_get_cached_plugin_info()
class CarlaCachedPluginInfo(Structure):
_fields_ = [
# Plugin category.
("category", c_enum),

# Plugin hints.
# @see PluginHints
("hints", c_uint),

# Number of audio inputs.
("audioIns", c_uint32),

# Number of audio outputs.
("audioOuts", c_uint32),

# Number of MIDI inputs.
("midiIns", c_uint32),

# Number of MIDI outputs.
("midiOuts", c_uint32),

# Number of input parameters.
("parameterIns", c_uint32),

# Number of output parameters.
("parameterOuts", c_uint32),

# Plugin name.
("name", c_char_p),

# Plugin label.
("label", c_char_p),

# Plugin author/maker.
("maker", c_char_p),

# Plugin copyright/license.
("copyright", c_char_p)
]

# Port count information, used for Audio and MIDI ports and parameters.
# @see carla_get_audio_port_count_info()
# @see carla_get_midi_port_count_info()
@@ -1186,22 +1144,6 @@ PyCarlaPluginInfo = {
'uniqueId': 0
}

# @see CarlaCachedPluginInfo
PyCarlaCachedPluginInfo = {
'category': PLUGIN_CATEGORY_NONE,
'hints': 0x0,
'audioIns': 0,
'audioOuts': 0,
'midiIns': 0,
'midiOuts': 0,
'parameterIns': 0,
'parameterOuts': 0,
'name': "",
'label': "",
'maker': "",
'copyright': ""
}

# @see CarlaPortCountInfo
PyCarlaPortCountInfo = {
'ins': 0,
@@ -1299,26 +1241,6 @@ class CarlaHostMeta(object):
keyrm = "%s=" % key
self.msvcrt._putenv(keyrm.encode("utf-8"))

# Get the complete license text of used third-party code and features.
# Returned string is in basic html format.
@abstractmethod
def get_complete_license_text(self):
raise NotImplementedError

# Get the juce version used in the current Carla build.
@abstractmethod
def get_juce_version(self):
raise NotImplementedError

# Get all the supported file extensions in carla_load_file().
# Returned string uses this syntax:
# @code
# "*.ext1;*.ext2;*.ext3"
# @endcode
@abstractmethod
def get_supported_file_extensions(self):
raise NotImplementedError

# Get how many engine drivers are available.
@abstractmethod
def get_engine_driver_count(self):
@@ -1343,16 +1265,6 @@ class CarlaHostMeta(object):
def get_engine_driver_device_info(self, index, name):
raise NotImplementedError

# Get how many internal plugins are available.
@abstractmethod
def get_cached_plugin_count(self, ptype, pluginPath):
raise NotImplementedError

# Get information about a cached plugin.
@abstractmethod
def get_cached_plugin_info(self, ptype, index):
raise NotImplementedError

# Initialize the engine.
# Make sure to call carla_engine_idle() at regular intervals afterwards.
# @param driverName Driver to use
@@ -1925,53 +1837,6 @@ class CarlaHostNull(CarlaHostMeta):
self.fEngineCallback = None
self.fEngineRunning = False

def get_complete_license_text(self):
text = (
"<p>This current Carla build is using the following features and 3rd-party code:</p>"
"<ul>"

# Plugin formats
"<li>LADSPA plugin support</li>"
"<li>DSSI plugin support</li>"
"<li>LV2 plugin support</li>"
"<li>VST2 plugin support using official VST SDK 2.4 [1]</li>"
"<li>VST3 plugin support using official VST SDK 3.6 [1]</li>"
"<li>AU plugin support</li>"

# Sample kit libraries
"<li>FluidSynth library for SF2 support</li>"
"<li>LinuxSampler library for GIG and SFZ support [2]</li>"

# Internal plugins
"<li>NekoFilter plugin code based on lv2fil by Nedko Arnaudov and Fons Adriaensen</li>"
"<li>ZynAddSubFX plugin code</li>"

# misc libs
"<li>base64 utilities based on code by Ren\u00E9 Nyffenegger</li>"
"<li>sem_timedwait for Mac OS by Keith Shortridge</li>"
"<li>liblo library for OSC support</li>"
"<li>rtmempool library by Nedko Arnaudov"
"<li>serd, sord, sratom and lilv libraries for LV2 discovery</li>"
"<li>RtAudio and RtMidi libraries for extra Audio and MIDI support</li>"

# end
"</ul>"

"<p>"
# Required by VST SDK
"&nbsp;[1] Trademark of Steinberg Media Technologies GmbH.<br/>"
# LinuxSampler GPL exception
"&nbsp;[2] Using LinuxSampler code in commercial hardware or software products is not allowed without prior written authorization by the authors."
"</p>"
)
return text

def get_juce_version(self):
return "3.0"

def get_supported_file_extensions(self):
return "*.carxp;*.carxs;*.mid;*.midi;*.sf2;*.gig;*.sfz;*.xmz;*.xiz"

def get_engine_driver_count(self):
return 0

@@ -1984,12 +1849,6 @@ class CarlaHostNull(CarlaHostMeta):
def get_engine_driver_device_info(self, index, name):
return PyEngineDriverDeviceInfo

def get_cached_plugin_count(self, ptype, pluginPath):
return 0

def get_cached_plugin_info(self, ptype, index):
return PyCarlaCachedPluginInfo

def engine_init(self, driverName, clientName):
self.fEngineRunning = True
if self.fEngineCallback is not None:
@@ -2251,15 +2110,6 @@ class CarlaHostDLL(CarlaHostMeta):

self.lib = cdll.LoadLibrary(libName)

self.lib.carla_get_complete_license_text.argtypes = None
self.lib.carla_get_complete_license_text.restype = c_char_p

self.lib.carla_get_juce_version.argtypes = None
self.lib.carla_get_juce_version.restype = c_char_p

self.lib.carla_get_supported_file_extensions.argtypes = None
self.lib.carla_get_supported_file_extensions.restype = c_char_p

self.lib.carla_get_engine_driver_count.argtypes = None
self.lib.carla_get_engine_driver_count.restype = c_uint

@@ -2272,12 +2122,6 @@ class CarlaHostDLL(CarlaHostMeta):
self.lib.carla_get_engine_driver_device_info.argtypes = [c_uint, c_char_p]
self.lib.carla_get_engine_driver_device_info.restype = POINTER(EngineDriverDeviceInfo)

self.lib.carla_get_cached_plugin_count.argtypes = [c_enum, c_char_p]
self.lib.carla_get_cached_plugin_count.restype = c_uint

self.lib.carla_get_cached_plugin_info.argtypes = [c_enum, c_uint]
self.lib.carla_get_cached_plugin_info.restype = POINTER(CarlaCachedPluginInfo)

self.lib.carla_engine_init.argtypes = [c_char_p, c_char_p]
self.lib.carla_engine_init.restype = c_bool

@@ -2523,15 +2367,6 @@ class CarlaHostDLL(CarlaHostMeta):

# --------------------------------------------------------------------------------------------------------

def get_complete_license_text(self):
return charPtrToString(self.lib.carla_get_complete_license_text())

def get_juce_version(self):
return charPtrToString(self.lib.carla_get_juce_version())

def get_supported_file_extensions(self):
return charPtrToString(self.lib.carla_get_supported_file_extensions())

def get_engine_driver_count(self):
return int(self.lib.carla_get_engine_driver_count())

@@ -2544,12 +2379,6 @@ class CarlaHostDLL(CarlaHostMeta):
def get_engine_driver_device_info(self, index, name):
return structToDict(self.lib.carla_get_engine_driver_device_info(index, name.encode("utf-8")).contents)

def get_cached_plugin_count(self, ptype, pluginPath):
return int(self.lib.carla_get_cached_plugin_count(ptype, pluginPath.encode("utf-8")))

def get_cached_plugin_info(self, ptype, index):
return structToDict(self.lib.carla_get_cached_plugin_info(ptype, index).contents)

def engine_init(self, driverName, clientName):
return bool(self.lib.carla_engine_init(driverName.encode("utf-8"), clientName.encode("utf-8")))

@@ -2836,11 +2665,8 @@ class CarlaHostPlugin(CarlaHostMeta):
self.processModeForced = True

# text data to return when requested
self.fCompleteLicenseText = ""
self.fJuceVersion = ""
self.fSupportedFileExts = ""
self.fMaxPluginNumber = 0
self.fLastError = ""
self.fMaxPluginNumber = 0
self.fLastError = ""

# plugin info
self.fPluginsInfo = []
@@ -2876,15 +2702,6 @@ class CarlaHostPlugin(CarlaHostMeta):

# --------------------------------------------------------------------------------------------------------

def get_complete_license_text(self):
return self.fCompleteLicenseText

def get_juce_version(self):
return self.fJuceVersion

def get_supported_file_extensions(self):
return self.fSupportedFileExts

def get_engine_driver_count(self):
return 1

@@ -2897,12 +2714,6 @@ class CarlaHostPlugin(CarlaHostMeta):
def get_engine_driver_device_info(self, index, name):
return PyEngineDriverDeviceInfo

def get_cached_plugin_count(self, ptype, pluginPath):
return 0

def get_cached_plugin_info(self, ptype, index):
return PyCarlaCachedPluginInfo

def set_engine_callback(self, func):
return # TODO

@@ -3143,12 +2954,6 @@ class CarlaHostPlugin(CarlaHostMeta):

# --------------------------------------------------------------------------------------------------------

def _set_info(self, license, juceversion, fileexts, maxnum):
self.fCompleteLicenseText = license
self.fJuceVersion = juceversion
self.fSupportedFileExts = fileexts
self.fMaxPluginNumber = maxnum

def _set_transport(self, playing, frame, bar, beat, tick, bpm):
self.fTransportInfo = {
"playing": playing,


+ 6
- 6
source/carla_database.py View File

@@ -1482,9 +1482,9 @@ class PluginDatabaseW(QDialog):
AU_PATH = splitter.join(toList(settings.value(CARLA_KEY_PATHS_AU, CARLA_DEFAULT_AU_PATH)))
del settings

internalCountNew = self.host.get_cached_plugin_count(PLUGIN_INTERNAL, "")
lv2CountNew = self.host.get_cached_plugin_count(PLUGIN_LV2, LV2_PATH)
auCountNew = self.host.get_cached_plugin_count(PLUGIN_AU, AU_PATH)
internalCountNew = gCarla.utils.get_cached_plugin_count(PLUGIN_INTERNAL, "")
lv2CountNew = gCarla.utils.get_cached_plugin_count(PLUGIN_LV2, LV2_PATH)
auCountNew = gCarla.utils.get_cached_plugin_count(PLUGIN_AU, AU_PATH)

if internalCountNew != internalCount or (len(internalPlugins) > 0 and
len(internalPlugins[0]) > 0 and
@@ -1492,7 +1492,7 @@ class PluginDatabaseW(QDialog):
internalPlugins = []

for i in range(internalCountNew):
descInfo = self.host.get_cached_plugin_info(PLUGIN_INTERNAL, i)
descInfo = gCarla.utils.get_cached_plugin_info(PLUGIN_INTERNAL, i)
plugins = checkPluginCached(descInfo, PLUGIN_INTERNAL)

if plugins:
@@ -1506,7 +1506,7 @@ class PluginDatabaseW(QDialog):
lv2Plugins = []

for i in range(lv2CountNew):
descInfo = self.host.get_cached_plugin_info(PLUGIN_LV2, i)
descInfo = gCarla.utils.get_cached_plugin_info(PLUGIN_LV2, i)
plugins = checkPluginCached(descInfo, PLUGIN_LV2)

if plugins:
@@ -1520,7 +1520,7 @@ class PluginDatabaseW(QDialog):
auPlugins = []

for i in range(auCountNew):
descInfo = self.host.get_cached_plugin_info(PLUGIN_AU, i)
descInfo = gCarla.utils.get_cached_plugin_info(PLUGIN_AU, i)
plugins = checkPluginCached(descInfo, PLUGIN_AU)

if plugins:


+ 2
- 2
source/carla_host.py View File

@@ -207,7 +207,7 @@ class HostWindow(QMainWindow):

self.fDirModel = QFileSystemModel(self)
self.fDirModel.setRootPath(HOME)
self.fDirModel.setNameFilters(host.get_supported_file_extensions().split(";"))
self.fDirModel.setNameFilters(gCarla.utils.get_supported_file_extensions().split(";"))

self.ui.fileTreeView.setModel(self.fDirModel)
self.ui.fileTreeView.setRootIndex(self.fDirModel.index(HOME))
@@ -1328,7 +1328,7 @@ class HostWindow(QMainWindow):

@pyqtSlot()
def slot_aboutJuce(self):
JuceAboutW(self, self.host).exec_()
JuceAboutW(self).exec_()

@pyqtSlot()
def slot_aboutQt(self):


+ 98
- 15
source/carla_utils.py View File

@@ -88,11 +88,72 @@ def getPluginTypeFromString(stype):
return PLUGIN_NONE

# ------------------------------------------------------------------------------------------------------------
# Carla Pipe stuff
# Carla Utils API (C stuff)

CarlaPipeClientHandle = c_void_p
CarlaPipeCallbackFunc = CFUNCTYPE(None, c_void_p, c_char_p)

# Information about an internal Carla plugin.
# @see carla_get_cached_plugin_info()
class CarlaCachedPluginInfo(Structure):
_fields_ = [
# Plugin category.
("category", c_enum),

# Plugin hints.
# @see PluginHints
("hints", c_uint),

# Number of audio inputs.
("audioIns", c_uint32),

# Number of audio outputs.
("audioOuts", c_uint32),

# Number of MIDI inputs.
("midiIns", c_uint32),

# Number of MIDI outputs.
("midiOuts", c_uint32),

# Number of input parameters.
("parameterIns", c_uint32),

# Number of output parameters.
("parameterOuts", c_uint32),

# Plugin name.
("name", c_char_p),

# Plugin label.
("label", c_char_p),

# Plugin author/maker.
("maker", c_char_p),

# Plugin copyright/license.
("copyright", c_char_p)
]

# ------------------------------------------------------------------------------------------------------------
# Carla Utils API (Python compatible stuff)

# @see CarlaCachedPluginInfo
PyCarlaCachedPluginInfo = {
'category': PLUGIN_CATEGORY_NONE,
'hints': 0x0,
'audioIns': 0,
'audioOuts': 0,
'midiIns': 0,
'midiOuts': 0,
'parameterIns': 0,
'parameterOuts': 0,
'name': "",
'label': "",
'maker': "",
'copyright': ""
}

# ------------------------------------------------------------------------------------------------------------
# Carla Utils object using a DLL

@@ -102,14 +163,20 @@ class CarlaUtils(object):

self.lib = cdll.LoadLibrary(filename)

self.lib.carla_get_library_filename.argtypes = None
self.lib.carla_get_library_filename.restype = c_char_p
self.lib.carla_get_complete_license_text.argtypes = None
self.lib.carla_get_complete_license_text.restype = c_char_p

self.lib.carla_get_library_folder.argtypes = None
self.lib.carla_get_library_folder.restype = c_char_p
self.lib.carla_get_juce_version.argtypes = None
self.lib.carla_get_juce_version.restype = c_char_p

self.lib.carla_set_locale_C.argtypes = None
self.lib.carla_set_locale_C.restype = None
self.lib.carla_get_supported_file_extensions.argtypes = None
self.lib.carla_get_supported_file_extensions.restype = c_char_p

self.lib.carla_get_cached_plugin_count.argtypes = [c_enum, c_char_p]
self.lib.carla_get_cached_plugin_count.restype = c_uint

self.lib.carla_get_cached_plugin_info.argtypes = [c_enum, c_uint]
self.lib.carla_get_cached_plugin_info.restype = POINTER(CarlaCachedPluginInfo)

self.lib.carla_set_process_name.argtypes = [c_char_p]
self.lib.carla_set_process_name.restype = None
@@ -149,14 +216,30 @@ class CarlaUtils(object):

# --------------------------------------------------------------------------------------------------------

def get_library_filename(self):
return charPtrToString(self.lib.carla_get_library_filename())

def get_library_folder(self):
return charPtrToString(self.lib.carla_get_library_folder())

def set_locale_C(self):
self.lib.carla_set_locale_C()
# Get the complete license text of used third-party code and features.
# Returned string is in basic html format.
def get_complete_license_text(self):
return charPtrToString(self.lib.carla_get_complete_license_text())

# Get the juce version used in the current Carla build.
def get_juce_version(self):
return charPtrToString(self.lib.carla_get_juce_version())

# Get all the supported file extensions in carla_load_file().
# Returned string uses this syntax:
# @code
# "*.ext1;*.ext2;*.ext3"
# @endcode
def get_supported_file_extensions(self):
return charPtrToString(self.lib.carla_get_supported_file_extensions())

# Get how many internal plugins are available.
def get_cached_plugin_count(self, ptype, pluginPath):
return int(self.lib.carla_get_cached_plugin_count(ptype, pluginPath.encode("utf-8")))

# Get information about a cached plugin.
def get_cached_plugin_info(self, ptype, index):
return structToDict(self.lib.carla_get_cached_plugin_info(ptype, index).contents)

def set_process_name(self, name):
self.lib.carla_set_process_name(name.encode("utf-8"))


+ 3
- 7
source/carla_widgets.py View File

@@ -87,7 +87,7 @@ class CarlaAboutW(QDialog):
elif host.isPlugin:
self.ui.tabWidget.removeTab(2)

self.ui.l_extended.setText(host.get_complete_license_text())
self.ui.l_extended.setText(gCarla.utils.get_complete_license_text())

if host.is_engine_running() and not (host.isControl or host.isPlugin):
self.ui.le_osc_url_tcp.setText(host.get_host_osc_url_tcp())
@@ -172,16 +172,12 @@ class CarlaAboutW(QDialog):
# JUCE About dialog

class JuceAboutW(QDialog):
def __init__(self, parent, host):
def __init__(self, parent):
QDialog.__init__(self, parent)
self.ui = ui_carla_about_juce.Ui_JuceAboutW()
self.ui.setupUi(self)

if False:
# kdevelop likes this :)
host = CarlaHostMeta()

self.ui.l_text2.setText(self.tr("This program uses JUCE version %s." % host.get_juce_version()))
self.ui.l_text2.setText(self.tr("This program uses JUCE version %s." % gCarla.utils.get_juce_version()))

self.adjustSize()
self.setFixedSize(self.size())


+ 10
- 2
source/plugin/carla-base.cpp View File

@@ -33,6 +33,14 @@

using CarlaBackend::CarlaPlugin;

// -----------------------------------------------------------------------

CARLA_EXTERN_C
std::size_t carla_getNativePluginCount();

CARLA_EXTERN_C
const NativePluginDescriptor* carla_getNativePluginDescriptor(const std::size_t index);

// -----------------------------------------------------------------------
// Plugin List

@@ -49,9 +57,9 @@ struct PluginListManager {
#endif
descs()
{
for (size_t i=0, count = CarlaPlugin::getNativePluginCount(); i < count; ++i)
for (std::size_t i=0, count = carla_getNativePluginCount(); i < count; ++i)
{
const NativePluginDescriptor* const desc(CarlaPlugin::getNativePluginDescriptor(i));
const NativePluginDescriptor* const desc(carla_getNativePluginDescriptor(i));

// Open/Save not possible in plugins
if (desc->hints & NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE)


+ 0
- 1
source/utils/CarlaBackendUtils.hpp View File

@@ -19,7 +19,6 @@
#define CARLA_BACKEND_UTILS_HPP_INCLUDED

#include "CarlaBackend.h"
#include "CarlaHost.h"
#include "CarlaString.hpp"

CARLA_BACKEND_START_NAMESPACE


+ 10
- 12
source/widgets/racklistwidget.py View File

@@ -134,7 +134,16 @@ class RackListWidget(QListWidget):
host = CarlaHostMeta()
self.host = host

self.fSupportedExtensions = []
exts = gCarla.utils.get_supported_file_extensions().split(";")

exts.append(".dll")

if MACOS:
exts.append(".dylib")
if not WINDOWS:
exts.append(".so")

self.fSupportedExtensions = tuple(i.replace("*","") for i in exts)
self.fWasLastDragValid = False

self.setMinimumWidth(640)
@@ -162,17 +171,6 @@ class RackListWidget(QListWidget):
def setHost(self, host):
self.host = host

exts = host.get_supported_file_extensions().split(";")

exts.append(".dll")

if MACOS:
exts.append(".dylib")
if not WINDOWS:
exts.append(".so")

self.fSupportedExtensions = tuple(i.replace("*","") for i in exts)

# --------------------------------------------------------------------------------------------------------

def isDragUrlValid(self, url):


Loading…
Cancel
Save