Browse Source

Misc progress

tags/1.9.4
falkTX 11 years ago
parent
commit
b723bdfdaa
7 changed files with 250 additions and 181 deletions
  1. +10
    -0
      data/carla-standalone.pc
  2. +16
    -10
      source/Makefile.mk
  3. +5
    -5
      source/discovery/Makefile
  4. +6
    -6
      source/discovery/carla-discovery.cpp
  5. +41
    -16
      source/utils/CarlaLv2Utils.hpp
  6. +1
    -1
      source/utils/CarlaRingBuffer.hpp
  7. +171
    -143
      source/utils/CarlaStateUtils.hpp

+ 10
- 0
data/carla-standalone.pc View File

@@ -0,0 +1,10 @@
prefix=X-PREFIX-X
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include

Name: carla-standalone
Version: 1.9.1
Description: Carla Host Standalone
Libs: -L${libdir}
Cflags: -I${includedir}/carla -I${includedir}/carla/includes

+ 16
- 10
source/Makefile.mk View File

@@ -85,10 +85,10 @@ ifneq ($(shell pkg-config --exists liblo && echo true),true)
$(error liblo missing, cannot continue) $(error liblo missing, cannot continue)
endif endif


# Qt5 always required
ifneq ($(shell pkg-config --exists Qt5Core Qt5Gui Qt5Widgets && echo true),true)
$(error liblo missing, cannot continue)
endif
# Qt always required
# ifneq ($(shell pkg-config --exists QtCore QtGui QtXml && echo true),true)
# $(error Qt4 missing, cannot continue)
# endif


# -------------------------------------------------------------- # --------------------------------------------------------------
# Check for optional libs (required by backend or bridges) # Check for optional libs (required by backend or bridges)
@@ -101,7 +101,8 @@ HAVE_GTK2 = $(shell pkg-config --exists gtk+-2.0 && echo true)
HAVE_GTK3 = $(shell pkg-config --exists gtk+-3.0 && echo true) HAVE_GTK3 = $(shell pkg-config --exists gtk+-3.0 && echo true)
HAVE_OPENGL = $(shell pkg-config --exists gl && echo true) HAVE_OPENGL = $(shell pkg-config --exists gl && echo true)
HAVE_PULSEAUDIO = $(shell pkg-config --exists libpulse-simple && echo true) HAVE_PULSEAUDIO = $(shell pkg-config --exists libpulse-simple && echo true)
HAVE_QT4 = $(shell pkg-config --exists QtCore QtGui && echo true)
HAVE_QT4 = $(shell pkg-config --exists QtCore QtGui QtXml && echo true)
HAVE_QT5 = $(shell pkg-config --exists Qt5Core Qt5Gui Qt5Widgets Qt5Xml && echo true)
else else
HAVE_OPENGL = true HAVE_OPENGL = true
endif endif
@@ -174,8 +175,10 @@ JUCE_GUI_BASICS_FLAGS = $(shell pkg-config --cflags x11 xinerama xext xcursor
JUCE_GUI_BASICS_LIBS = $(shell pkg-config --libs x11 xinerama xext xcursor) -ldl JUCE_GUI_BASICS_LIBS = $(shell pkg-config --libs x11 xinerama xext xcursor) -ldl
endif endif
LILV_LIBS = -ldl -lm -lrt LILV_LIBS = -ldl -lm -lrt
QTCORE_FLAGS = $(shell pkg-config --cflags Qt5Core)
QTCORE_LIBS = $(shell pkg-config --libs Qt5Core)
QTCORE_FLAGS = $(shell pkg-config --cflags QtCore)
QTCORE_LIBS = $(shell pkg-config --libs QtCore)
QTXML_FLAGS = $(shell pkg-config --cflags QtXml)
QTXML_LIBS = $(shell pkg-config --libs QtXml)
ifeq ($(HAVE_ALSA),true) ifeq ($(HAVE_ALSA),true)
RTAUDIO_FLAGS += $(shell pkg-config --cflags alsa) -D__LINUX_ALSA__ RTAUDIO_FLAGS += $(shell pkg-config --cflags alsa) -D__LINUX_ALSA__
RTAUDIO_LIBS += $(shell pkg-config --libs alsa) -lpthread RTAUDIO_LIBS += $(shell pkg-config --libs alsa) -lpthread
@@ -198,7 +201,8 @@ JUCE_CORE_LIBS = -framework Cocoa -framework IOKit
JUCE_GRAPHICS_LIBS = -framework Cocoa -framework QuartzCore JUCE_GRAPHICS_LIBS = -framework Cocoa -framework QuartzCore
JUCE_GUI_BASICS_LIBS = -framework Cocoa -framework Carbon -framework QuartzCore JUCE_GUI_BASICS_LIBS = -framework Cocoa -framework Carbon -framework QuartzCore
LILV_LIBS = -ldl -lm LILV_LIBS = -ldl -lm
QTCORE_LIBS = -framework Qt5Core
QTCORE_LIBS = -framework QtCore
QTXML_LIBS = -framework QtXml
RTAUDIO_FLAGS += -D__MACOSX_CORE__ RTAUDIO_FLAGS += -D__MACOSX_CORE__
RTAUDIO_LIBS += -lpthread RTAUDIO_LIBS += -lpthread
RTMIDI_FLAGS += -D__MACOSX_CORE__ RTMIDI_FLAGS += -D__MACOSX_CORE__
@@ -213,8 +217,10 @@ JUCE_EVENTS_LIBS = -lole32
JUCE_GRAPHICS_LIBS = -lgdi32 JUCE_GRAPHICS_LIBS = -lgdi32
JUCE_GUI_BASICS_LIBS = -lgdi32 -limm32 -lcomdlg32 -lole32 JUCE_GUI_BASICS_LIBS = -lgdi32 -limm32 -lcomdlg32 -lole32
LILV_LIBS = -lm LILV_LIBS = -lm
QTCORE_FLAGS = $(shell pkg-config --cflags Qt5Core)
QTCORE_LIBS = $(shell pkg-config --libs Qt5Core)
QTCORE_FLAGS = $(shell pkg-config --cflags QtCore)
QTCORE_LIBS = $(shell pkg-config --libs QtCore)
QTXML_FLAGS = $(shell pkg-config --cflags QtXml)
QTXML_LIBS = $(shell pkg-config --libs QtXml)
RTAUDIO_FLAGS += -D__WINDOWS_ASIO__ -D__WINDOWS_DS__ RTAUDIO_FLAGS += -D__WINDOWS_ASIO__ -D__WINDOWS_DS__
RTAUDIO_LIBS += -lpthread RTAUDIO_LIBS += -lpthread
RTMIDI_FLAGS += -D__WINDOWS_MM__ RTMIDI_FLAGS += -D__WINDOWS_MM__


+ 5
- 5
source/discovery/Makefile View File

@@ -17,19 +17,19 @@ LINK_FLAGS += $(QTCORE_LIBS)
BUILD_CXX_FLAGS += -DWANT_NATIVE BUILD_CXX_FLAGS += -DWANT_NATIVE


ifeq ($(CARLA_PLUGIN_SUPPORT),true) ifeq ($(CARLA_PLUGIN_SUPPORT),true)
BUILD_CXX_FLAGS += -DWANT_LADSPA -DWANT_VST
BUILD_CXX_FLAGS += -DWANT_LADSPA -DWANT_LV2 -DWANT_VST
ifeq ($(CARLA_VESTIGE_HEADER),true) ifeq ($(CARLA_VESTIGE_HEADER),true)
BUILD_CXX_FLAGS += -DVESTIGE_HEADER BUILD_CXX_FLAGS += -DVESTIGE_HEADER
endif endif
endif endif


# -DWANT_DSSI -DWANT_LV2
# -DWANT_DSSI


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


ifeq ($(HAVE_CSOUND),true) ifeq ($(HAVE_CSOUND),true)
NATIVE_FLAGS += $(shell pkg-config --cflags --libs sndfile) -lcsound64 -DWANT_CSOUND
NATIVE_FLAGS += $(JUCE_CORE_FLAGS) $(JUCE_CORE_LIBS)
# NATIVE_FLAGS += $(shell pkg-config --cflags --libs sndfile) -lcsound64 -DWANT_CSOUND
# NATIVE_FLAGS += $(JUCE_CORE_FLAGS) $(JUCE_CORE_LIBS)
endif endif


ifeq ($(HAVE_FLUIDSYNTH),true) ifeq ($(HAVE_FLUIDSYNTH),true)
@@ -52,7 +52,7 @@ LINK_FLAGS += $(LILV_LIBS)
endif endif


ifeq ($(HAVE_CSOUND),true) ifeq ($(HAVE_CSOUND),true)
LIBS = ../modules/juce_core.a
# LIBS = ../modules/juce_core.a
endif endif


POSIX_BUILD_FLAGS = $(BUILD_CXX_FLAGS) POSIX_BUILD_FLAGS = $(BUILD_CXX_FLAGS)


+ 6
- 6
source/discovery/carla-discovery.cpp View File

@@ -946,30 +946,30 @@ void do_lv2_check(const char* const bundle, const bool init)
const Lilv::Plugins lilvPlugins(lv2World.get_all_plugins()); const Lilv::Plugins lilvPlugins(lv2World.get_all_plugins());


// Get all plugin URIs in this bundle // Get all plugin URIs in this bundle
juce::StringArray URIs;
QStringList URIs;


LILV_FOREACH(plugins, i, lilvPlugins) LILV_FOREACH(plugins, i, lilvPlugins)
{ {
Lilv::Plugin lilvPlugin(lilv_plugins_get(lilvPlugins, i)); Lilv::Plugin lilvPlugin(lilv_plugins_get(lilvPlugins, i));


if (const char* const uri = lilvPlugin.get_uri().as_string()) if (const char* const uri = lilvPlugin.get_uri().as_string())
URIs.add(juce::String(uri));
URIs.append(QString(uri));
} }


if (URIs.size() == 0)
if (URIs.count() == 0)
{ {
DISCOVERY_OUT("warning", "LV2 Bundle doesn't provide any plugins"); DISCOVERY_OUT("warning", "LV2 Bundle doesn't provide any plugins");
return; return;
} }


// Get & check every plugin-instance // Get & check every plugin-instance
for (juce::String* it = URIs.begin(); it != URIs.end(); ++it)
for (int i=0, count=URIs.count(); i < count; ++i)
{ {
const LV2_RDF_Descriptor* const rdfDescriptor(lv2_rdf_new(it->toRawUTF8(), false));
const LV2_RDF_Descriptor* const rdfDescriptor(lv2_rdf_new(URIs.at(i).toUtf8().constData(), false));


if (rdfDescriptor == nullptr || rdfDescriptor->URI == nullptr) if (rdfDescriptor == nullptr || rdfDescriptor->URI == nullptr)
{ {
DISCOVERY_OUT("error", "Failed to find LV2 plugin '" << it->toRawUTF8() << "'");
DISCOVERY_OUT("error", "Failed to find LV2 plugin '" << URIs.at(i).toUtf8().constData() << "'");
continue; continue;
} }




+ 41
- 16
source/utils/CarlaLv2Utils.hpp View File

@@ -18,7 +18,7 @@
#ifndef CARLA_LV2_UTILS_HPP_INCLUDED #ifndef CARLA_LV2_UTILS_HPP_INCLUDED
#define CARLA_LV2_UTILS_HPP_INCLUDED #define CARLA_LV2_UTILS_HPP_INCLUDED


#include "CarlaJuceUtils.hpp"
#include "CarlaUtils.hpp"


#include "lv2/lv2.h" #include "lv2/lv2.h"
#include "lv2/atom.h" #include "lv2/atom.h"
@@ -61,9 +61,8 @@
#include "lilv/lilvmm.hpp" #include "lilv/lilvmm.hpp"
#include "sratom/sratom.h" #include "sratom/sratom.h"


#ifdef USE_JUCE
#include "juce_core.h"
#endif
#include <QtCore/QMap>
#include <QtCore/QStringList>


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Define namespaces and missing prefixes // Define namespaces and missing prefixes
@@ -79,9 +78,6 @@


#define LV2_MIDI_LL__MidiPort "http://ll-plugins.nongnu.org/lv2/ext/MidiPort" #define LV2_MIDI_LL__MidiPort "http://ll-plugins.nongnu.org/lv2/ext/MidiPort"


#define LV2_OSC__OscEvent "http://kxstudio.sf.net/ns/lv2ext/osc#OscEvent"

#define LV2_UI__NtkUI LV2_UI_PREFIX "NtkUI"
#define LV2_UI__Qt5UI LV2_UI_PREFIX "Qt5UI" #define LV2_UI__Qt5UI LV2_UI_PREFIX "Qt5UI"
#define LV2_UI__idle LV2_UI_PREFIX "idle" #define LV2_UI__idle LV2_UI_PREFIX "idle"
#define LV2_UI__makeResident LV2_UI_PREFIX "makeResident" #define LV2_UI__makeResident LV2_UI_PREFIX "makeResident"
@@ -91,7 +87,7 @@


struct LV2_Atom_MidiEvent { struct LV2_Atom_MidiEvent {
LV2_Atom_Event event; LV2_Atom_Event event;
uint8_t data[8];
uint8_t data[4];
}; };


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@@ -183,7 +179,6 @@ public:
Lilv::Node ui_gtk3; Lilv::Node ui_gtk3;
Lilv::Node ui_qt4; Lilv::Node ui_qt4;
Lilv::Node ui_qt5; Lilv::Node ui_qt5;
Lilv::Node ui_ntk;
Lilv::Node ui_cocoa; Lilv::Node ui_cocoa;
Lilv::Node ui_windows; Lilv::Node ui_windows;
Lilv::Node ui_x11; Lilv::Node ui_x11;
@@ -205,7 +200,6 @@ public:


// Port Data Types // Port Data Types
Lilv::Node midi_event; Lilv::Node midi_event;
Lilv::Node osc_event;
Lilv::Node patch_message; Lilv::Node patch_message;
Lilv::Node time_position; Lilv::Node time_position;


@@ -301,7 +295,6 @@ public:
ui_gtk3 (new_uri(LV2_UI__Gtk3UI)), ui_gtk3 (new_uri(LV2_UI__Gtk3UI)),
ui_qt4 (new_uri(LV2_UI__Qt4UI)), ui_qt4 (new_uri(LV2_UI__Qt4UI)),
ui_qt5 (new_uri(LV2_UI__Qt5UI)), ui_qt5 (new_uri(LV2_UI__Qt5UI)),
ui_ntk (new_uri(LV2_UI__NtkUI)),
ui_cocoa (new_uri(LV2_UI__CocoaUI)), ui_cocoa (new_uri(LV2_UI__CocoaUI)),
ui_windows (new_uri(LV2_UI__WindowsUI)), ui_windows (new_uri(LV2_UI__WindowsUI)),
ui_x11 (new_uri(LV2_UI__X11UI)), ui_x11 (new_uri(LV2_UI__X11UI)),
@@ -321,7 +314,6 @@ public:
value_maximum (new_uri(LV2_CORE__maximum)), value_maximum (new_uri(LV2_CORE__maximum)),


midi_event (new_uri(LV2_MIDI__MidiEvent)), midi_event (new_uri(LV2_MIDI__MidiEvent)),
osc_event (new_uri(LV2_OSC__OscEvent)),
patch_message (new_uri(LV2_PATCH__Message)), patch_message (new_uri(LV2_PATCH__Message)),
time_position (new_uri(LV2_TIME__Position)), time_position (new_uri(LV2_TIME__Position)),


@@ -552,7 +544,7 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool fillPresets)


if (replaceNode.is_uri()) if (replaceNode.is_uri())
{ {
#ifdef USE_JUCE
#if 0//def HAVE_JUCE
const juce::String replaceURI(replaceNode.as_uri()); const juce::String replaceURI(replaceNode.as_uri());


if (replaceURI.startsWith("urn:")) if (replaceURI.startsWith("urn:"))
@@ -560,6 +552,19 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool fillPresets)
if (int uniqueId = replaceURI.getTrailingIntValue()) if (int uniqueId = replaceURI.getTrailingIntValue())
rdfDescriptor->UniqueID = (unsigned long)uniqueId; rdfDescriptor->UniqueID = (unsigned long)uniqueId;
} }
#else
const QString replaceURI(replaceNode.as_uri());

if (replaceURI.startsWith("urn:"))
{
const QString replaceId(replaceURI.split(":").last());

bool ok;
const ulong uniqueId(replaceId.toULong(&ok));

if (ok && uniqueId != 0)
rdfDescriptor->UniqueID = uniqueId;
}
#endif #endif
} }
} }
@@ -1007,7 +1012,6 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool fillPresets)
} }
} }


#ifdef USE_JUCE
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Set Plugin Presets // Set Plugin Presets


@@ -1018,23 +1022,42 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool fillPresets)
if (presetNodes.size() > 0) if (presetNodes.size() > 0)
{ {
// create a list of preset URIs (for checking appliesTo, sorting and unique-ness) // create a list of preset URIs (for checking appliesTo, sorting and unique-ness)
#if 0//def HAVE_JUCE
juce::StringArray presetListURIs; juce::StringArray presetListURIs;
#else
QStringList presetListURIs;
#endif


LILV_FOREACH(nodes, j, presetNodes) LILV_FOREACH(nodes, j, presetNodes)
{ {
Lilv::Node presetNode(presetNodes.get(j)); Lilv::Node presetNode(presetNodes.get(j));
// FIXME - check appliesTo? // FIXME - check appliesTo?


#if 0//def HAVE_JUCE
juce::String presetURI(presetNode.as_uri()); juce::String presetURI(presetNode.as_uri());


if (presetURI.trim().isNotEmpty()) if (presetURI.trim().isNotEmpty())
presetListURIs.addIfNotAlreadyThere(presetURI); presetListURIs.addIfNotAlreadyThere(presetURI);
#else
QString presetURI(presetNode.as_uri());

if (! (presetURI.trimmed().isEmpty() || presetListURIs.contains(presetURI)))
presetListURIs.append(presetURI);
#endif
} }


#if 0//def HAVE_JUCE
presetListURIs.sort(false); presetListURIs.sort(false);
#else
presetListURIs.sort();
#endif


// create presets with unique URIs // create presets with unique URIs
#if 0//def HAVE_JUCE
rdfDescriptor->PresetCount = static_cast<uint32_t>(presetListURIs.size()); rdfDescriptor->PresetCount = static_cast<uint32_t>(presetListURIs.size());
#else
rdfDescriptor->PresetCount = static_cast<uint32_t>(presetListURIs.count());
#endif
rdfDescriptor->Presets = new LV2_RDF_Preset[rdfDescriptor->PresetCount]; rdfDescriptor->Presets = new LV2_RDF_Preset[rdfDescriptor->PresetCount];


// set preset data // set preset data
@@ -1047,8 +1070,11 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool fillPresets)


if (const char* const presetURI = presetNode.as_uri()) if (const char* const presetURI = presetNode.as_uri())
{ {
#if 0//def HAVE_JUCE
const int index(presetListURIs.indexOf(juce::String(presetURI))); const int index(presetListURIs.indexOf(juce::String(presetURI)));

#else
const int index(presetListURIs.indexOf(QString(presetURI)));
#endif
CARLA_SAFE_ASSERT_CONTINUE(index >= 0); CARLA_SAFE_ASSERT_CONTINUE(index >= 0);


LV2_RDF_Preset* const rdfPreset(&rdfDescriptor->Presets[index]); LV2_RDF_Preset* const rdfPreset(&rdfDescriptor->Presets[index]);
@@ -1070,7 +1096,6 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool fillPresets)
} }
} }
} }
#endif


// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Set Plugin Features // Set Plugin Features


+ 1
- 1
source/utils/CarlaRingBuffer.hpp View File

@@ -99,7 +99,7 @@ public:


char readChar() char readChar()
{ {
char c = 0;
char c = '\0';
tryRead(&c, sizeof(char)); tryRead(&c, sizeof(char));
return c; return c;
} }


+ 171
- 143
source/utils/CarlaStateUtils.hpp View File

@@ -22,9 +22,7 @@
#include "CarlaMIDI.h" #include "CarlaMIDI.h"
#include "List.hpp" #include "List.hpp"


#ifdef USE_JUCE
# include "juce_core.h"
#endif
#include <QtXml/QDomNode>


CARLA_BACKEND_START_NAMESPACE CARLA_BACKEND_START_NAMESPACE


@@ -213,13 +211,12 @@ struct SaveState {
CARLA_DECLARE_NON_COPY_STRUCT(SaveState) CARLA_DECLARE_NON_COPY_STRUCT(SaveState)
}; };


#ifdef USE_JUCE
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------


static inline static inline
juce::String xmlSafeString(const juce::String& string, const bool toXml)
QString xmlSafeString(const QString& string, const bool toXml)
{ {
juce::String newString(string);
QString newString(string);


if (toXml) if (toXml)
return newString.replace("&","&amp;").replace("<","&lt;").replace(">","&gt;").replace("'","&apos;").replace("\"","&quot;"); return newString.replace("&","&amp;").replace("<","&lt;").replace(">","&gt;").replace("'","&apos;").replace("\"","&quot;");
@@ -228,97 +225,124 @@ juce::String xmlSafeString(const juce::String& string, const bool toXml)
} }


static inline static inline
const char* xmlSafeStringCharDup(const juce::String& string, const bool toXml)
const char* xmlSafeStringCharDup(const QString& string, const bool toXml)
{ {
return carla_strdup(xmlSafeString(string, toXml).toRawUTF8());
return carla_strdup(xmlSafeString(string, toXml).toUtf8().constData());
} }


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


static inline static inline
void fillSaveStateFromXmlElement(SaveState& saveState, const juce::XmlElement* const xmlElement)
void fillSaveStateFromXmlNode(SaveState& saveState, const QDomNode& xmlNode)
{ {
using namespace juce;
if (xmlNode.isNull())
return saveState;


for (XmlElement* elem = xmlElement->getFirstChildElement(); elem != nullptr; elem = elem->getNextElement())
for (QDomNode node = xmlNode.firstChild(); ! node.isNull(); node = node.nextSibling())
{ {
QString tagName(node.toElement().tagName());

// --------------------------------------------------------------- // ---------------------------------------------------------------
// Info // Info


if (elem->getTagName().equalsIgnoreCase("info"))
if (tagName.compare("info", Qt::CaseInsensitive) == 0)
{ {
for (XmlElement* xmlInfo = elem->getFirstChildElement(); xmlInfo != nullptr; xmlInfo = xmlInfo->getNextElement())
for (QDomNode xmlInfo = node.toElement().firstChild(); ! xmlInfo.isNull(); xmlInfo = xmlInfo.nextSibling())
{ {
const String& tag(xmlInfo->getTagName());
const String text(xmlInfo->getAllSubText().trim());
const QString tag(xmlInfo.toElement().tagName());
const QString text(xmlInfo.toElement().text().trimmed());


if (tag.equalsIgnoreCase("type"))
if (tag.compare("type", Qt::CaseInsensitive) == 0)
{
saveState.type = xmlSafeStringCharDup(text, false); saveState.type = xmlSafeStringCharDup(text, false);
else if (tag.equalsIgnoreCase("name"))
}
else if (tag.compare("name", Qt::CaseInsensitive) == 0)
{
saveState.name = xmlSafeStringCharDup(text, false); saveState.name = xmlSafeStringCharDup(text, false);
else if (tag.equalsIgnoreCase("label") || tag.equalsIgnoreCase("uri"))
}
else if (tag.compare("label", Qt::CaseInsensitive) == 0 || tag.compare("uri", Qt::CaseInsensitive) == 0)
{
saveState.label = xmlSafeStringCharDup(text, false); saveState.label = xmlSafeStringCharDup(text, false);
else if (tag.equalsIgnoreCase("binary") || tag.equalsIgnoreCase("filename"))
}
else if (tag.compare("binary", Qt::CaseInsensitive) == 0 || tag.compare("filename", Qt::CaseInsensitive) == 0)
{
saveState.binary = xmlSafeStringCharDup(text, false); saveState.binary = xmlSafeStringCharDup(text, false);
else if (tag.equalsIgnoreCase("uniqueid"))
saveState.uniqueID = text.getLargeIntValue();
}
else if (tag.compare("uniqueid", Qt::CaseInsensitive) == 0)
{
bool ok;
const long uniqueID(text.toLong(&ok));
if (ok) saveState.uniqueID = uniqueID;
}
} }
} }


// --------------------------------------------------------------- // ---------------------------------------------------------------
// Data // Data


else if (elem->getTagName().equalsIgnoreCase("data"))
else if (tagName.compare("data", Qt::CaseInsensitive) == 0)
{ {
for (XmlElement* xmlData = elem->getFirstChildElement(); xmlData != nullptr; xmlData = xmlData->getNextElement())
for (QDomNode xmlData = node.toElement().firstChild(); ! xmlData.isNull(); xmlData = xmlData.nextSibling())
{ {
const String& tag(xmlData->getTagName());
const String text(xmlData->getAllSubText().trim());
const QString tag(xmlData.toElement().tagName());
const QString text(xmlData.toElement().text().trimmed());


// ------------------------------------------------------- // -------------------------------------------------------
// Internal Data // Internal Data


if (tag.equalsIgnoreCase("active"))
if (tag.compare("active", Qt::CaseInsensitive) == 0)
{ {
saveState.active = (text.equalsIgnoreCase("yes"));
saveState.active = (text.compare("yes", Qt::CaseInsensitive) == 0 || text.compare("true", Qt::CaseInsensitive) == 0);
} }
else if (tag.equalsIgnoreCase("drywet"))
else if (tag.compare("drywet", Qt::CaseInsensitive) == 0)
{ {
saveState.dryWet = carla_fixValue(0.0f, 1.0f, text.getFloatValue());
bool ok;
const float value(text.toFloat(&ok));
if (ok) saveState.dryWet = carla_fixValue(0.0f, 1.0f, value);
} }
else if (tag.equalsIgnoreCase("volume"))
else if (tag.compare("volume", Qt::CaseInsensitive) == 0)
{ {
saveState.volume = carla_fixValue(0.0f, 1.27f, text.getFloatValue());
bool ok;
const float value(text.toFloat(&ok));
if (ok) saveState.volume = carla_fixValue(0.0f, 1.27f, value);
} }
else if (tag.equalsIgnoreCase("balanceleft") || tag.equalsIgnoreCase("balance-left"))
else if (tag.compare("balanceleft", Qt::CaseInsensitive) == 0 || tag.compare("balance-left", Qt::CaseInsensitive) == 0)
{ {
saveState.balanceLeft = carla_fixValue(-1.0f, 1.0f, text.getFloatValue());
bool ok;
const float value(text.toFloat(&ok));
if (ok) saveState.balanceLeft = carla_fixValue(-1.0f, 1.0f, value);
} }
else if (tag.equalsIgnoreCase("balanceright") || tag.equalsIgnoreCase("balance-right"))
else if (tag.compare("balanceright", Qt::CaseInsensitive) == 0 || tag.compare("balance-right", Qt::CaseInsensitive) == 0)
{ {
saveState.balanceRight = carla_fixValue(-1.0f, 1.0f, text.getFloatValue());
bool ok;
const float value(text.toFloat(&ok));
if (ok) saveState.balanceRight = carla_fixValue(-1.0f, 1.0f, value);
} }
else if (tag.equalsIgnoreCase("panning"))
else if (tag.compare("panning", Qt::CaseInsensitive) == 0)
{ {
saveState.panning = carla_fixValue(-1.0f, 1.0f, text.getFloatValue());
bool ok;
const float value(text.toFloat(&ok));
if (ok) saveState.panning = carla_fixValue(-1.0f, 1.0f, value);
} }
else if (tag.equalsIgnoreCase("controlchannel") || tag.equalsIgnoreCase("control-channel"))
else if (tag.compare("controlchannel", Qt::CaseInsensitive) == 0 || tag.compare("control-channel", Qt::CaseInsensitive) == 0)
{ {
const int value(text.getIntValue());
if (value >= 1 && value <= MAX_MIDI_CHANNELS)
bool ok;
const short value(text.toShort(&ok));
if (ok && value >= 1 && value < MAX_MIDI_CHANNELS)
saveState.ctrlChannel = static_cast<int8_t>(value-1); saveState.ctrlChannel = static_cast<int8_t>(value-1);
} }


// ------------------------------------------------------- // -------------------------------------------------------
// Program (current) // Program (current)


else if (tag.equalsIgnoreCase("currentprogramindex") || tag.equalsIgnoreCase("current-program-index"))
else if (tag.compare("currentprogramindex", Qt::CaseInsensitive) == 0 || tag.compare("current-program-index", Qt::CaseInsensitive) == 0)
{ {
const int value(text.getIntValue());
if (value >= 1)
bool ok;
const int value(text.toInt(&ok));
if (ok && value >= 1)
saveState.currentProgramIndex = value-1; saveState.currentProgramIndex = value-1;
} }
else if (tag.equalsIgnoreCase("currentprogramname") || tag.equalsIgnoreCase("current-program-name"))
else if (tag.compare("currentprogramname", Qt::CaseInsensitive) == 0 || tag.compare("current-program-name", Qt::CaseInsensitive) == 0)
{ {
saveState.currentProgramName = xmlSafeStringCharDup(text, false); saveState.currentProgramName = xmlSafeStringCharDup(text, false);
} }
@@ -326,59 +350,65 @@ void fillSaveStateFromXmlElement(SaveState& saveState, const juce::XmlElement* c
// ------------------------------------------------------- // -------------------------------------------------------
// Midi Program (current) // Midi Program (current)


else if (tag.equalsIgnoreCase("currentmidibank") || tag.equalsIgnoreCase("current-midi-bank"))
else if (tag.compare("currentmidibank", Qt::CaseInsensitive) == 0 || tag.compare("current-midi-bank", Qt::CaseInsensitive) == 0)
{ {
const int value(text.getIntValue());
if (value >= 1)
bool ok;
const int value(text.toInt(&ok));
if (ok && value >= 1)
saveState.currentMidiBank = value-1; saveState.currentMidiBank = value-1;
} }
else if (tag.equalsIgnoreCase("currentmidiprogram") || tag.equalsIgnoreCase("current-midi-program"))
else if (tag.compare("currentmidiprogram", Qt::CaseInsensitive) == 0 || tag.compare("current-midi-program", Qt::CaseInsensitive) == 0)
{ {
const int value(text.getIntValue());
if (value >= 1)
bool ok;
const int value(text.toInt(&ok));
if (ok && value >= 1)
saveState.currentMidiProgram = value-1; saveState.currentMidiProgram = value-1;
} }


// ------------------------------------------------------- // -------------------------------------------------------
// Parameters // Parameters


else if (tag.equalsIgnoreCase("parameter"))
else if (tag.compare("parameter", Qt::CaseInsensitive) == 0)
{ {
StateParameter* const stateParameter(new StateParameter()); StateParameter* const stateParameter(new StateParameter());


for (XmlElement* xmlSubData = xmlData->getFirstChildElement(); xmlSubData != nullptr; xmlSubData = xmlSubData->getNextElement())
for (QDomNode xmlSubData = xmlData.toElement().firstChild(); ! xmlSubData.isNull(); xmlSubData = xmlSubData.nextSibling())
{ {
const String& pTag(xmlSubData->getTagName());
const String pText(xmlSubData->getAllSubText().trim());
const QString pTag(xmlSubData.toElement().tagName());
const QString pText(xmlSubData.toElement().text().trimmed());


if (pTag.equalsIgnoreCase("index"))
if (pTag.compare("index", Qt::CaseInsensitive) == 0)
{ {
const int index(pText.getIntValue());
if (index >= 0)
stateParameter->index = static_cast<uint32_t>(index);
bool ok;
const uint index(pText.toUInt(&ok));
if (ok) stateParameter->index = index;
} }
else if (pTag.equalsIgnoreCase("name"))
else if (pTag.compare("name", Qt::CaseInsensitive) == 0)
{ {
stateParameter->name = xmlSafeStringCharDup(pText, false); stateParameter->name = xmlSafeStringCharDup(pText, false);
} }
else if (pTag.equalsIgnoreCase("symbol"))
else if (pTag.compare("symbol", Qt::CaseInsensitive) == 0)
{ {
stateParameter->symbol = xmlSafeStringCharDup(pText, false); stateParameter->symbol = xmlSafeStringCharDup(pText, false);
} }
else if (pTag.equalsIgnoreCase("value"))
else if (pTag.compare("value", Qt::CaseInsensitive) == 0)
{ {
stateParameter->value = pText.getFloatValue();
bool ok;
const float value(pText.toFloat(&ok));
if (ok) stateParameter->value = value;
} }
else if (pTag.equalsIgnoreCase("midichannel") || pTag.equalsIgnoreCase("midi-channel"))
else if (pTag.compare("midichannel", Qt::CaseInsensitive) == 0 || pTag.compare("midi-channel", Qt::CaseInsensitive) == 0)
{ {
const int channel(pText.getIntValue());
if (channel >= 1 && channel <= MAX_MIDI_CHANNELS)
bool ok;
const ushort channel(pText.toUShort(&ok));
if (ok && channel >= 1 && channel < MAX_MIDI_CHANNELS)
stateParameter->midiChannel = static_cast<uint8_t>(channel-1); stateParameter->midiChannel = static_cast<uint8_t>(channel-1);
} }
else if (pTag.equalsIgnoreCase("midicc") || pTag.equalsIgnoreCase("midi-cc"))
else if (pTag.compare("midicc", Qt::CaseInsensitive) == 0 || pTag.compare("midi-cc", Qt::CaseInsensitive) == 0)
{ {
const int cc(pText.getIntValue());
if (cc >= 1 && cc < 0x5F)
bool ok;
const int cc(pText.toInt(&ok));
if (ok && cc >= 1 && cc < 0x5F)
stateParameter->midiCC = static_cast<int16_t>(cc); stateParameter->midiCC = static_cast<int16_t>(cc);
} }
} }
@@ -389,20 +419,20 @@ void fillSaveStateFromXmlElement(SaveState& saveState, const juce::XmlElement* c
// ------------------------------------------------------- // -------------------------------------------------------
// Custom Data // Custom Data


else if (tag.equalsIgnoreCase("customdata") || tag.equalsIgnoreCase("custom-data"))
else if (tag.compare("customdata", Qt::CaseInsensitive) == 0 || tag.compare("custom-data", Qt::CaseInsensitive) == 0)
{ {
StateCustomData* const stateCustomData(new StateCustomData()); StateCustomData* const stateCustomData(new StateCustomData());


for (XmlElement* xmlSubData = xmlData->getFirstChildElement(); xmlSubData != nullptr; xmlSubData = xmlSubData->getNextElement())
for (QDomNode xmlSubData = xmlData.toElement().firstChild(); ! xmlSubData.isNull(); xmlSubData = xmlSubData.nextSibling())
{ {
const String& cTag(xmlSubData->getTagName());
const String cText(xmlSubData->getAllSubText().trim());
const QString cTag(xmlSubData.toElement().tagName());
const QString cText(xmlSubData.toElement().text().trimmed());


if (cTag.equalsIgnoreCase("type"))
if (cTag.compare("type", Qt::CaseInsensitive) == 0)
stateCustomData->type = xmlSafeStringCharDup(cText, false); stateCustomData->type = xmlSafeStringCharDup(cText, false);
else if (cTag.equalsIgnoreCase("key"))
else if (cTag.compare("key", Qt::CaseInsensitive) == 0)
stateCustomData->key = xmlSafeStringCharDup(cText, false); stateCustomData->key = xmlSafeStringCharDup(cText, false);
else if (cTag.equalsIgnoreCase("value"))
else if (cTag.compare("value", Qt::CaseInsensitive) == 0)
stateCustomData->value = xmlSafeStringCharDup(cText, false); stateCustomData->value = xmlSafeStringCharDup(cText, false);
} }


@@ -412,7 +442,7 @@ void fillSaveStateFromXmlElement(SaveState& saveState, const juce::XmlElement* c
// ------------------------------------------------------- // -------------------------------------------------------
// Chunk // Chunk


else if (tag.equalsIgnoreCase("chunk"))
else if (tag.compare("chunk", Qt::CaseInsensitive) == 0)
{ {
saveState.chunk = xmlSafeStringCharDup(text, false); saveState.chunk = xmlSafeStringCharDup(text, false);
} }
@@ -424,164 +454,162 @@ void fillSaveStateFromXmlElement(SaveState& saveState, const juce::XmlElement* c
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------


static inline static inline
void fillXmlStringFromSaveState(juce::String& content, const SaveState& saveState)
void fillXmlStringFromSaveState(QString& content, const SaveState& saveState)
{ {
using namespace juce;

{ {
String info(" <Info>\n");
QString info(" <Info>\n");


info << " <Type>" << saveState.type << "</Type>\n";
info << " <Name>" << xmlSafeString(saveState.name, true) << "</Name>\n";
info += QString(" <Type>%1</Type>\n").arg(saveState.type);
info += QString(" <Name>%1</Name>\n").arg(xmlSafeString(saveState.name, true));


switch (getPluginTypeFromString(saveState.type)) switch (getPluginTypeFromString(saveState.type))
{ {
case PLUGIN_NONE: case PLUGIN_NONE:
break; break;
case PLUGIN_INTERNAL: case PLUGIN_INTERNAL:
info << " <Label>" << xmlSafeString(saveState.label, true) << "</Label>\n";
info += QString(" <Label>%1</Label>\n").arg(xmlSafeString(saveState.label, true));
break; break;
case PLUGIN_LADSPA: case PLUGIN_LADSPA:
info << " <Binary>" << xmlSafeString(saveState.binary, true) << "</Binary>\n";
info << " <Label>" << xmlSafeString(saveState.label, true) << "</Label>\n";
info << " <UniqueID>" << saveState.uniqueID << "</UniqueID>\n";
info += QString(" <Binary>%1</Binary>\n").arg(xmlSafeString(saveState.binary, true));
info += QString(" <Label>%1</Label>\n").arg(xmlSafeString(saveState.label, true));
info += QString(" <UniqueID>%1</UniqueID>\n").arg(saveState.uniqueID);
break; break;
case PLUGIN_DSSI: case PLUGIN_DSSI:
info << " <Binary>" << xmlSafeString(saveState.binary, true) << "</Binary>\n";
info << " <Label>" << xmlSafeString(saveState.label, true) << "</Label>\n";
info += QString(" <Binary>%1</Binary>\n").arg(xmlSafeString(saveState.binary, true));
info += QString(" <Label>%1</Label>\n").arg(xmlSafeString(saveState.label, true));
break; break;
case PLUGIN_LV2: case PLUGIN_LV2:
info << " <URI>" << xmlSafeString(saveState.label, true) << "</URI>\n";
info += QString(" <URI>%1</URI>\n").arg(xmlSafeString(saveState.label, true));
break; break;
case PLUGIN_VST: case PLUGIN_VST:
info << " <Binary>" << xmlSafeString(saveState.binary, true) << "</Binary>\n";
info << " <UniqueID>" << saveState.uniqueID << "</UniqueID>\n";
info += QString(" <Binary>%1</Binary>\n").arg(xmlSafeString(saveState.binary, true));
info += QString(" <UniqueID>%1</UniqueID>\n").arg(saveState.uniqueID);
break; break;
case PLUGIN_AU: case PLUGIN_AU:
// TODO? // TODO?
info << " <Binary>" << xmlSafeString(saveState.binary, true) << "</Binary>\n";
info << " <UniqueID>" << saveState.uniqueID << "</UniqueID>\n";
info += QString(" <Binary>%1</Binary>\n").arg(xmlSafeString(saveState.binary, true));
info += QString(" <UniqueID>%1</UniqueID>\n").arg(saveState.uniqueID);
break; break;
case PLUGIN_CSOUND: case PLUGIN_CSOUND:
case PLUGIN_GIG: case PLUGIN_GIG:
case PLUGIN_SF2: case PLUGIN_SF2:
case PLUGIN_SFZ: case PLUGIN_SFZ:
info << " <Filename>" << xmlSafeString(saveState.binary, true) << "</Filename>\n";
info << " <Label>" << xmlSafeString(saveState.label, true) << "</Label>\n";
info += QString(" <Filename>%1</Filename>\n").arg(xmlSafeString(saveState.binary, true));
info += QString(" <Label>%1</Label>\n").arg(xmlSafeString(saveState.label, true));
break; break;
} }


info << " </Info>\n\n";
info += " </Info>\n\n";


content << info;
content += info;
} }


{ {
String data(" <Data>\n");
QString data(" <Data>\n");


data << " <Active>" << (saveState.active ? "Yes" : "No") << "</Active>\n";
data += QString(" <Active>%1</Active>\n").arg(saveState.active ? "Yes" : "No");


if (saveState.dryWet != 1.0f) if (saveState.dryWet != 1.0f)
data << " <DryWet>" << saveState.dryWet << "</DryWet>\n";
data += QString(" <DryWet>%1</DryWet>\n").arg(saveState.dryWet);
if (saveState.volume != 1.0f) if (saveState.volume != 1.0f)
data << " <Volume>" << saveState.volume << "</Volume>\n";
data += QString(" <Volume>%1</Volume>\n").arg(saveState.volume);
if (saveState.balanceLeft != -1.0f) if (saveState.balanceLeft != -1.0f)
data << " <Balance-Left>" << saveState.balanceLeft << "</Balance-Left>\n";
data += QString(" <Balance-Left>%1</Balance-Left>\n").arg(saveState.balanceLeft);
if (saveState.balanceRight != 1.0f) if (saveState.balanceRight != 1.0f)
data << " <Balance-Right>" << saveState.balanceRight << "</Balance-Right>\n";
data += QString(" <Balance-Right>%1</Balance-Right>\n").arg(saveState.balanceRight);
if (saveState.panning != 0.0f) if (saveState.panning != 0.0f)
data << " <Panning>" << saveState.panning << "</Panning>\n";
data += QString(" <Panning>%1</Panning>\n").arg(saveState.panning);


if (saveState.ctrlChannel < 0) if (saveState.ctrlChannel < 0)
data << " <ControlChannel>N</ControlChannel>\n";
data += QString(" <ControlChannel>N</ControlChannel>\n");
else else
data << " <ControlChannel>" << saveState.ctrlChannel+1 << "</ControlChannel>\n";
data += QString(" <ControlChannel>%1</ControlChannel>\n").arg(saveState.ctrlChannel+1);


content << data;
content += data;
} }


for (StateParameterItenerator it = saveState.parameters.begin(); it.valid(); it.next()) for (StateParameterItenerator it = saveState.parameters.begin(); it.valid(); it.next())
{ {
StateParameter* const stateParameter(*it); StateParameter* const stateParameter(*it);


String parameter("\n"" <Parameter>\n");
QString parameter("\n"" <Parameter>\n");


parameter << " <Index>" << (long)stateParameter->index << "</Index>\n"; // FIXME
parameter << " <Name>" << xmlSafeString(stateParameter->name, true) << "</Name>\n";
parameter += QString(" <Index>%1</Index>\n").arg(stateParameter->index);
parameter += QString(" <Name>%1</Name>\n").arg(xmlSafeString(stateParameter->name, true));


if (stateParameter->symbol != nullptr && stateParameter->symbol[0] != '\0') if (stateParameter->symbol != nullptr && stateParameter->symbol[0] != '\0')
parameter << " <Symbol>" << xmlSafeString(stateParameter->symbol, true) << "</Symbol>\n";
parameter += QString(" <Symbol>%1</Symbol>\n").arg(xmlSafeString(stateParameter->symbol, true));


parameter << " <Value>" << stateParameter->value << "</Value>\n";
parameter += QString(" <Value>%1</Value>\n").arg(stateParameter->value);


if (stateParameter->midiCC > 0) if (stateParameter->midiCC > 0)
{ {
parameter << " <MidiCC>" << stateParameter->midiCC << "</MidiCC>\n";
parameter << " <MidiChannel>" << stateParameter->midiChannel+1 << "</MidiChannel>\n";
parameter += QString(" <MidiCC>%1</MidiCC>\n").arg(stateParameter->midiCC);
parameter += QString(" <MidiChannel>%1</MidiChannel>\n").arg(stateParameter->midiChannel+1);
} }


parameter << " </Parameter>\n";
parameter += " </Parameter>\n";


content << parameter;
content += parameter;
} }


if (saveState.currentProgramIndex >= 0 && saveState.currentProgramName != nullptr)
if (saveState.currentProgramIndex >= 0 && saveState.currentProgramName != nullptr && saveState.currentProgramName[0] != '\0')
{ {
// ignore 'default' program // ignore 'default' program
#ifdef __USE_GNU
if ((saveState.currentProgramIndex > 0 || strcasecmp(saveState.currentProgramName, "default") != 0))
#else
if ((saveState.currentProgramIndex > 0 || std::strcmp(saveState.currentProgramName, "Default") != 0))
#endif
if (saveState.currentProgramIndex > 0 || QString(saveState.currentProgramName).compare("default", Qt::CaseInsensitive) != 0)
{ {
String program("\n");
program << " <CurrentProgramIndex>" << saveState.currentProgramIndex+1 << "</CurrentProgramIndex>\n";
program << " <CurrentProgramName>" << xmlSafeString(saveState.currentProgramName, true) << "</CurrentProgramName>\n";
QString program("\n");
program += QString(" <CurrentProgramIndex>%1</CurrentProgramIndex>\n").arg(saveState.currentProgramIndex+1);
program += QString(" <CurrentProgramName>%1</CurrentProgramName>\n").arg(xmlSafeString(saveState.currentProgramName, true));


content << program;
content += program;
} }
} }


if (saveState.currentMidiBank >= 0 && saveState.currentMidiProgram >= 0) if (saveState.currentMidiBank >= 0 && saveState.currentMidiProgram >= 0)
{ {
String midiProgram("\n");
midiProgram << " <CurrentMidiBank>" << saveState.currentMidiBank+1 << "</CurrentMidiBank>\n";
midiProgram << " <CurrentMidiProgram>" << saveState.currentMidiProgram+1 << "</CurrentMidiProgram>\n";
QString midiProgram("\n");
midiProgram += QString(" <CurrentMidiBank>%1</CurrentMidiBank>\n").arg(saveState.currentMidiBank+1);
midiProgram += QString(" <CurrentMidiProgram>%1</CurrentMidiProgram>\n").arg(saveState.currentMidiProgram+1);


content << midiProgram;
content += midiProgram;
} }


for (StateCustomDataItenerator it = saveState.customData.begin(); it.valid(); it.next()) for (StateCustomDataItenerator it = saveState.customData.begin(); it.valid(); it.next())
{ {
StateCustomData* const stateCustomData(*it); StateCustomData* const stateCustomData(*it);


String customData("\n"" <CustomData>\n");
customData << " <Type>" << xmlSafeString(stateCustomData->type, true) << "</Type>\n";
customData << " <Key>" << xmlSafeString(stateCustomData->key, true) << "</Key>\n";
QString customData("\n"" <CustomData>\n");
customData += QString(" <Type>%1</Type>\n").arg(xmlSafeString(stateCustomData->type, true));
customData += QString(" <Key>%1</Key>\n").arg(xmlSafeString(stateCustomData->key, true));


if (std::strcmp(stateCustomData->type, CUSTOM_DATA_CHUNK) == 0 || std::strlen(stateCustomData->value) >= 128)
customData << " <Value>\n" << xmlSafeString(stateCustomData->value, true) << "\n </Value>\n";
if (std::strcmp(stateCustomData->type, CUSTOM_DATA_TYPE_CHUNK) == 0 || std::strlen(stateCustomData->value) >= 128)
{
customData += " <Value>\n";
customData += QString("%1\n").arg(xmlSafeString(stateCustomData->value, true));
customData += " </Value>\n";
}
else else
customData << " <Value>" << xmlSafeString(stateCustomData->value, true) << "</Value>\n";
customData += QString(" <Value>%1</Value>\n").arg(xmlSafeString(stateCustomData->value, true));


customData << " </CustomData>\n";
customData += " </CustomData>\n";


content << customData;
content += customData;
} }


if (saveState.chunk != nullptr && saveState.chunk[0] != '\0') if (saveState.chunk != nullptr && saveState.chunk[0] != '\0')
{ {
String chunk("\n"" <Chunk>\n");
chunk << saveState.chunk << "\n </Chunk>\n";
QString chunk("\n"" <Chunk>\n");
chunk += QString("%1\n").arg(saveState.chunk);
chunk += " </Chunk>\n";


content << chunk;
content += chunk;
} }


content << " </Data>\n";
content += " </Data>\n";
} }


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
#endif


CARLA_BACKEND_END_NAMESPACE CARLA_BACKEND_END_NAMESPACE




Loading…
Cancel
Save