Browse Source

Implement JSFX (backend)

tags/v2.5.0
Jean Pierre Cimalando 3 years ago
parent
commit
1f2f87ad41
14 changed files with 1554 additions and 6 deletions
  1. +3
    -2
      data/carla-single
  2. +1
    -0
      source/backend/CarlaPlugin.hpp
  3. +4
    -0
      source/backend/Makefile
  4. +6
    -4
      source/backend/engine/CarlaEngine.cpp
  5. +1072
    -0
      source/backend/plugin/CarlaPluginJSFX.cpp
  6. +6
    -0
      source/backend/plugin/Makefile
  7. +117
    -0
      source/backend/utils/CachedPlugins.cpp
  8. +5
    -0
      source/backend/utils/Information.cpp
  9. +4
    -0
      source/backend/utils/Makefile
  10. +9
    -0
      source/bridges-plugin/Makefile
  11. +5
    -0
      source/discovery/Makefile
  12. +69
    -0
      source/discovery/carla-discovery.cpp
  13. +6
    -0
      source/plugin/Makefile
  14. +247
    -0
      source/utils/CarlaJsfxUtils.hpp

+ 3
- 2
data/carla-single View File

@@ -50,6 +50,7 @@ Possible formats:
- lv2
- vst|vst2
- vst3
- jsfx
- sf2
- sfz

@@ -97,7 +98,7 @@ if len(sys.argv) == arg:
# --------------------------------------------------------------------------------------------------------
# Set format

if sys.argv[arg] in ("internal", "ladspa", "dssi", "lv2", "vst", "vst2", "vst3", "au", "sf2", "sfz"):
if sys.argv[arg] in ("internal", "ladspa", "dssi", "lv2", "vst", "vst2", "vst3", "au", "jsfx", "sf2", "sfz"):
FORMAT = sys.argv[arg]
arg += 1

@@ -193,7 +194,7 @@ if ARCH not in ("native", "posix32", "posix64", "win32", "win64"):
print("Invalid arch")
sys.exit(1)

if FORMAT not in ("internal", "ladspa", "dssi", "lv2", "vst2", "vst3", "au", "sf2", "sfz"):
if FORMAT not in ("internal", "ladspa", "dssi", "lv2", "vst2", "vst3", "au", "jsfx", "sf2", "sfz"):
print("Invalid format")
sys.exit(1)



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

@@ -973,6 +973,7 @@ public:
static CarlaPluginPtr newVST2(const Initializer& init);
static CarlaPluginPtr newVST3(const Initializer& init);
static CarlaPluginPtr newAU(const Initializer& init);
static CarlaPluginPtr newJSFX(const Initializer& init);

static CarlaPluginPtr newJuce(const Initializer& init, const char* format);
static CarlaPluginPtr newFluidSynth(const Initializer& init, PluginType ptype, bool use16Outs);


+ 4
- 0
source/backend/Makefile View File

@@ -65,6 +65,8 @@ STANDALONE_LIBS += $(MODULEDIR)/rtaudio.a
STANDALONE_LIBS += $(MODULEDIR)/rtmidi.a
endif

STANDALONE_LIBS += $(MODULEDIR)/ysfx.a

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

STANDALONE_LINK_FLAGS = $(HYLIA_LIBS)
@@ -100,6 +102,8 @@ STANDALONE_LINK_FLAGS += $(RTAUDIO_LIBS)
STANDALONE_LINK_FLAGS += $(RTMIDI_LIBS)
endif

STANDALONE_LINK_FLAGS += $(YSFX_GRAPHICS_LIBS)

ifeq ($(JACKBRIDGE_DIRECT),true)
STANDALONE_LINK_FLAGS += $(JACK_LIBS)
endif


+ 6
- 4
source/backend/engine/CarlaEngine.cpp View File

@@ -596,6 +596,7 @@ bool CarlaEngine::addPlugin(const BinaryType btype,
&& ptype != PLUGIN_GIG
&& ptype != PLUGIN_SF2
&& ptype != PLUGIN_SFZ
&& ptype != PLUGIN_JSFX
&& ptype != PLUGIN_JACK;

// Prefer bridges for some specific plugins
@@ -705,10 +706,6 @@ bool CarlaEngine::addPlugin(const BinaryType btype,
plugin = CarlaPlugin::newAU(initializer);
break;

case PLUGIN_JSFX:
setLastError("Not implemented yet");
break;

#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
case PLUGIN_INTERNAL:
plugin = CarlaPlugin::newNative(initializer);
@@ -741,6 +738,10 @@ bool CarlaEngine::addPlugin(const BinaryType btype,
# endif
break;

case PLUGIN_JSFX:
plugin = CarlaPlugin::newJSFX(initializer);
break;

case PLUGIN_JACK:
# ifndef STATIC_PLUGIN_TARGET
plugin = CarlaPlugin::newJackApp(initializer);
@@ -754,6 +755,7 @@ bool CarlaEngine::addPlugin(const BinaryType btype,
case PLUGIN_GIG:
case PLUGIN_SF2:
case PLUGIN_SFZ:
case PLUGIN_JSFX:
case PLUGIN_JACK:
setLastError("Plugin bridges cannot handle this binary");
break;


+ 1072
- 0
source/backend/plugin/CarlaPluginJSFX.cpp
File diff suppressed because it is too large
View File


+ 6
- 0
source/backend/plugin/Makefile View File

@@ -28,6 +28,7 @@ OBJS = \
$(OBJDIR)/CarlaPluginVST2.cpp.o \
$(OBJDIR)/CarlaPluginVST3.cpp.o \
$(OBJDIR)/CarlaPluginAU.cpp.o \
$(OBJDIR)/CarlaPluginJSFX.cpp.o \
$(OBJDIR)/CarlaPluginJuce.cpp.o \
$(OBJDIR)/CarlaPluginFluidSynth.cpp.o \
$(OBJDIR)/CarlaPluginSFZero.cpp.o
@@ -66,6 +67,11 @@ $(OBJDIR)/CarlaPluginFluidSynth.cpp.o: CarlaPluginFluidSynth.cpp
@echo "Compiling $<"
@$(CXX) $< $(BUILD_CXX_FLAGS) $(FLUIDSYNTH_FLAGS) -c -o $@

$(OBJDIR)/CarlaPluginJSFX.cpp.o: CarlaPluginJSFX.cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $<"
@$(CXX) $< $(BUILD_CXX_FLAGS) $(YSFX_FLAGS) -c -o $@

ifeq ($(MACOS),true)
$(OBJDIR)/CarlaPluginVST2.cpp.o: CarlaPluginVST2.cpp
-@mkdir -p $(OBJDIR)


+ 117
- 0
source/backend/utils/CachedPlugins.cpp View File

@@ -21,6 +21,7 @@
#include "CarlaString.hpp"
#include "CarlaBackendUtils.hpp"
#include "CarlaLv2Utils.hpp"
#include "CarlaJsfxUtils.hpp"

#if defined(USING_JUCE) && defined(CARLA_OS_MAC)
# include "AppConfig.h"
@@ -93,6 +94,37 @@ static void findSFZs(const char* const sfzPaths)
gSFZs.addArray(results);
}
}
// -------------------------------------------------------------------------------------------------------------------

static water::Array<CarlaJsfxUnit> gJSFXs;

static void findJSFXs(const char* const jsfxPaths)
{
gJSFXs.clearQuick();

CARLA_SAFE_ASSERT_RETURN(jsfxPaths != nullptr,);

if (jsfxPaths[0] == '\0')
return;

const water::StringArray splitPaths(water::StringArray::fromTokens(jsfxPaths, CARLA_OS_SPLIT_STR, ""));

for (water::String *it = splitPaths.begin(), *end = splitPaths.end(); it != end; ++it)
{
water::Array<water::File> results;
water::File path(*it);

if (path.findChildFiles(results, water::File::findFiles|water::File::ignoreHiddenFiles, true, "*") > 0)
{
for (const water::File& file : results)
{
water::String fileExt = file.getFileExtension();
if (fileExt.isEmpty() || fileExt.equalsIgnoreCase(".jsfx"))
gJSFXs.add(CarlaJsfxUnit(path, file));
}
}
}
}

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

@@ -627,6 +659,81 @@ static const CarlaCachedPluginInfo* get_cached_plugin_sfz(const water::File& fil

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

static const CarlaCachedPluginInfo* get_cached_plugin_jsfx(const CarlaJsfxUnit& unit)
{
static CarlaCachedPluginInfo info;

ysfx_config_u config(ysfx_config_new());

const water::String rootPath = unit.getRootPath().getFullPathName();
const water::String filePath = unit.getFilePath().getFullPathName();

ysfx_register_builtin_audio_formats(config.get());
ysfx_set_import_root(config.get(), rootPath.toRawUTF8());
ysfx_guess_file_roots(config.get(), filePath.toRawUTF8());
ysfx_set_log_reporter(config.get(), &CarlaJsfxLogging::logErrorsOnly);

ysfx_u effect(ysfx_new(config.get()));

if (! ysfx_load_file(effect.get(), filePath.toRawUTF8(), 0))
{
info.valid = false;
return &info;
}

// plugins with neither @block nor @sample are valid, but they are useless
// also use this as a sanity check against misdetected files
// since JSFX parsing is so permissive, it might accept lambda text files
if (! ysfx_has_section(effect.get(), ysfx_section_block) &&
! ysfx_has_section(effect.get(), ysfx_section_sample))
{
info.valid = false;
return &info;
}

static CarlaString name, label, maker;
label = unit.getFileId().toRawUTF8();
name = ysfx_get_name(effect.get());
maker = ysfx_get_author(effect.get());

info.valid = true;

info.category = CarlaJsfxCategories::getFromEffect(effect.get());

info.audioIns = ysfx_get_num_inputs(effect.get());
info.audioOuts = ysfx_get_num_outputs(effect.get());

info.cvIns = 0;
info.cvOuts = 0;

info.midiIns = 1;
info.midiOuts = 1;

info.parameterIns = 0;
info.parameterOuts = 0;
for (uint32_t sliderIndex = 0; sliderIndex < ysfx_max_sliders; ++sliderIndex)
{
if (ysfx_slider_exists(effect.get(), sliderIndex))
++info.parameterIns;
}

info.hints = 0;

#if 0 // TODO(jsfx) when supporting custom graphics
if (ysfx_has_section(effect.get(), ysfx_section_gfx))
info.hints |= CB::PLUGIN_HAS_CUSTOM_UI;
#endif

info.name = name.buffer();
info.label = label.buffer();
info.maker = maker.buffer();
info.copyright = gCachedPluginsNullCharPtr;

return &info;
}

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

uint carla_get_cached_plugin_count(CB::PluginType ptype, const char* pluginPath)
{
CARLA_SAFE_ASSERT_RETURN(isCachedPluginType(ptype), 0);
@@ -658,6 +765,11 @@ uint carla_get_cached_plugin_count(CB::PluginType ptype, const char* pluginPath)
return static_cast<uint>(gSFZs.size());
}

case CB::PLUGIN_JSFX: {
findJSFXs(pluginPath);
return static_cast<uint>(gJSFXs.size());
}

default:
return 0;
}
@@ -703,6 +815,11 @@ const CarlaCachedPluginInfo* carla_get_cached_plugin_info(CB::PluginType ptype,
return get_cached_plugin_sfz(gSFZs.getUnchecked(static_cast<int>(index)));
}

case CB::PLUGIN_JSFX: {
CARLA_SAFE_ASSERT_BREAK(index < static_cast<uint>(gJSFXs.size()));
return get_cached_plugin_jsfx(gJSFXs.getUnchecked(static_cast<int>(index)));
}

default:
break;
}


+ 5
- 0
source/backend/utils/Information.cpp View File

@@ -85,6 +85,8 @@ const char* carla_get_complete_license_text()
#endif
"<li>SFZero module for SFZ support</li>"

// TODO mention jsfx

// misc libs
"<li>base64 utilities based on code by Ren\u00E9 Nyffenegger</li>"
"<li>dr_mp3 for mp3 file support</li>"
@@ -188,6 +190,9 @@ const char* const* carla_get_supported_file_extensions()
// SFZ
"sfz",

// JSFX
"jsfx",

// terminator
nullptr
};


+ 4
- 0
source/backend/utils/Makefile View File

@@ -10,6 +10,7 @@ include ../Makefile.mk
# ---------------------------------------------------------------------------------------------------------------------

BUILD_CXX_FLAGS += $(FLUIDSYNTH_FLAGS)
BUILD_CXX_FLAGS += $(YSFX_FLAGS)

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

@@ -65,6 +66,9 @@ LINK_FLAGS += $(JUCE_GUI_EXTRA_LIBS)
endif
endif

LINK_FLAGS += $(MODULEDIR)/ysfx.a
LINK_FLAGS += $(YSFX_GRAPHICS_LIBS)

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

all: $(TARGETS)


+ 9
- 0
source/bridges-plugin/Makefile View File

@@ -135,6 +135,9 @@ LINK_FLAGS += $(JUCE_GUI_EXTRA_LIBS)
endif
endif

LIBS_native += $(MODULEDIR)/ysfx.a
LINK_FLAGS += $(YSFX_GRAPHICS_LIBS)

ifeq ($(JACKBRIDGE_DIRECT),true)
LINK_FLAGS += $(JACK_LIBS)
endif
@@ -193,6 +196,7 @@ OBJS_native = \
$(OBJDIR)/CarlaPluginVST2.cpp.o \
$(OBJDIR)/CarlaPluginVST3.cpp.o \
$(OBJDIR)/CarlaPluginAU.cpp.o \
$(OBJDIR)/CarlaPluginJSFX.cpp.o \
$(OBJDIR)/CarlaPluginJuce.cpp.o \
$(OBJDIR)/CarlaPluginFluidSynth.cpp.o \
$(OBJDIR)/CarlaPluginSFZero.cpp.o \
@@ -294,6 +298,11 @@ $(BINDIR)/$(MODULENAME)-win64.exe: $(OBJS_win64) $(LIBS_win64)
# ---------------------------------------------------------------------------------------------------------------------
# native

$(OBJDIR)/CarlaPluginJSFX.cpp.o: $(CWD)/backend/plugin/CarlaPluginJSFX.cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling CarlaPluginJSFX.cpp (bridge)"
@$(CXX) $< $(BUILD_CXX_FLAGS) $(NATIVE_BUILD_FLAGS) $(YSFX_FLAGS) -c -o $@

ifeq ($(MACOS),true)
$(OBJDIR)/CarlaPluginVST2.cpp.o: $(CWD)/backend/plugin/CarlaPluginVST2.cpp
-@mkdir -p $(OBJDIR)


+ 5
- 0
source/discovery/Makefile View File

@@ -59,6 +59,8 @@ NATIVE_BUILD_FLAGS += $(FLUIDSYNTH_FLAGS)
NATIVE_LINK_FLAGS += $(FLUIDSYNTH_LIBS)
endif

NATIVE_BUILD_FLAGS += $(YSFX_FLAGS)

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

LIBS_native = $(MODULEDIR)/lilv.a
@@ -138,6 +140,9 @@ LINK_FLAGS += $(JUCE_GUI_EXTRA_LIBS)
endif # USING_JUCE_GUI_EXTRA
endif # USING_JUCE

LIBS_native += $(MODULEDIR)/ysfx.a
LINK_FLAGS += $(YSFX_GRAPHICS_LIBS)

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

OBJS_native = $(OBJDIR)/$(MODULENAME).cpp.o


+ 69
- 0
source/discovery/carla-discovery.cpp View File

@@ -18,6 +18,7 @@
#include "CarlaBackendUtils.hpp"
#include "CarlaLibUtils.hpp"
#include "CarlaMathUtils.hpp"
#include "CarlaScopeUtils.hpp"
#include "CarlaMIDI.h"
#include "LinkedList.hpp"

@@ -86,6 +87,7 @@
#ifndef BUILD_BRIDGE
# include "water/text/StringArray.h"
# include "CarlaDssiUtils.cpp"
# include "CarlaJsfxUtils.hpp"
# include "../backend/utils/CachedPlugins.cpp"
#else
# include "CarlaDssiUtils.hpp"
@@ -1655,6 +1657,67 @@ static void do_fluidsynth_check(const char* const filename, const PluginType typ
#endif
}

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

#ifndef BUILD_BRIDGE
static void do_jsfx_check(const char* const filename, bool doInit)
{
const water::File file = File(CharPointer_UTF8(filename));

ysfx_config_u config(ysfx_config_new());

ysfx_register_builtin_audio_formats(config.get());
ysfx_guess_file_roots(config.get(), filename);
ysfx_set_log_reporter(config.get(), &CarlaJsfxLogging::logErrorsOnly);

ysfx_u effect(ysfx_new(config.get()));

uint hints = 0;

// do not attempt to compile it, because the import path is not known
(void)doInit;

if (! ysfx_load_file(effect.get(), filename, 0))
{
DISCOVERY_OUT("error", "Cannot read the JSFX header");
return;
}

const char* const name = ysfx_get_name(effect.get());

// author and category are extracted from the pseudo-tags
const char* const author = ysfx_get_author(effect.get());
const CB::PluginCategory category = CarlaJsfxCategories::getFromEffect(effect.get());

const uint32_t audioIns = ysfx_get_num_inputs(effect.get());
const uint32_t audioOuts = ysfx_get_num_outputs(effect.get());

const uint32_t midiIns = 1;
const uint32_t midiOuts = 1;

uint32_t parameters = 0;
for (uint32_t sliderIndex = 0; sliderIndex < ysfx_max_sliders; ++sliderIndex)
{
if (ysfx_slider_exists(effect.get(), sliderIndex))
++parameters;
}

DISCOVERY_OUT("init", "-----------");
DISCOVERY_OUT("build", BINARY_NATIVE);
DISCOVERY_OUT("hints", hints);
DISCOVERY_OUT("category", getPluginCategoryAsString(category));
DISCOVERY_OUT("name", name);
DISCOVERY_OUT("maker", author);
DISCOVERY_OUT("label", filename);
DISCOVERY_OUT("audio.ins", audioIns);
DISCOVERY_OUT("audio.outs", audioOuts);
DISCOVERY_OUT("midi.ins", midiIns);
DISCOVERY_OUT("midi.outs", midiOuts);
DISCOVERY_OUT("parameters.ins", parameters);
DISCOVERY_OUT("end", "------------");
}
#endif

// ------------------------------ main entry point ------------------------------

int main(int argc, char* argv[])
@@ -1825,6 +1888,12 @@ int main(int argc, char* argv[])
#endif
break;

#ifndef BUILD_BRIDGE
case PLUGIN_JSFX:
do_jsfx_check(filename, doInit);
break;
#endif

case PLUGIN_DLS:
case PLUGIN_GIG:
case PLUGIN_SF2:


+ 6
- 0
source/plugin/Makefile View File

@@ -44,6 +44,8 @@ ifeq ($(HAVE_FLUIDSYNTH),true)
BUILD_CXX_FLAGS += $(FLUIDSYNTH_FLAGS)
endif

BUILD_CXX_FLAGS += $(YSFX_FLAGS)

ifeq ($(HAVE_X11),true)
BUILD_CXX_FLAGS += $(X11_FLAGS)
endif
@@ -85,6 +87,8 @@ LIBS += $(MODULEDIR)/juce_gui_extra.a
endif
endif

LIBS += $(MODULEDIR)/ysfx.a

# ---------------------------------------------------------------------------------------------------------------------
# Link flags

@@ -114,6 +118,8 @@ LINK_FLAGS += $(JUCE_GUI_EXTRA_LIBS)
endif
endif

LINK_FLAGS += $(YSFX_GRAPHICS_LIBS)

ifeq ($(MACOS),true)
SYMBOLS_NATIVE = -Wl,-exported_symbol,_carla_get_native_rack_plugin
SYMBOLS_NATIVE += -Wl,-exported_symbol,_carla_get_native_patchbay_plugin


+ 247
- 0
source/utils/CarlaJsfxUtils.hpp View File

@@ -0,0 +1,247 @@
/*
* Carla JSFX utils
* Copyright (C) 2021 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

#ifndef CARLA_JSFX_UTILS_HPP_INCLUDED
#define CARLA_JSFX_UTILS_HPP_INCLUDED

#include "CarlaDefines.h"
#include "CarlaBackend.h"
#include "CarlaUtils.hpp"
#include "CarlaString.hpp"
#include "CarlaBase64Utils.hpp"
#include "CarlaJuceUtils.hpp"

#include "water/files/File.h"
#include "water/files/FileInputStream.h"
#include "water/xml/XmlElement.h"
#include "water/xml/XmlDocument.h"
#include "water/streams/MemoryInputStream.h"
#include "water/streams/MemoryOutputStream.h"

#include "ysfx.h"

#include <memory>

class CarlaJsfxLogging
{
public:
static void logAll(intptr_t, ysfx_log_level level, const char *message)
{
switch (level)
{
case ysfx_log_info:
carla_stdout("%s: %s", ysfx_log_level_string(level), message);
break;
case ysfx_log_warning:
case ysfx_log_error:
carla_stderr("%s: %s", ysfx_log_level_string(level), message);
break;
}
};

static void logErrorsOnly(intptr_t, ysfx_log_level level, const char *message)
{
switch (level)
{
case ysfx_log_info:
break;
case ysfx_log_warning:
case ysfx_log_error:
carla_stderr("%s: %s", ysfx_log_level_string(level), message);
break;
}
};
};

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

class CarlaJsfxCategories
{
public:
static CarlaBackend::PluginCategory getFromEffect(ysfx_t* effect)
{
CarlaBackend::PluginCategory category = CarlaBackend::PLUGIN_CATEGORY_OTHER;

water::Array<const char*> tags;
int tagCount = (int)ysfx_get_tags(effect, nullptr, 0);
tags.resize(tagCount);
ysfx_get_tags(effect, tags.getRawDataPointer(), (uint32_t)tagCount);

for (int i = 0; i < tagCount && category == CarlaBackend::PLUGIN_CATEGORY_OTHER; ++i)
{
water::CharPointer_UTF8 tag(tags[i]);
CarlaBackend::PluginCategory current = getFromTag(tag);
if (current != CarlaBackend::PLUGIN_CATEGORY_NONE)
category = current;
}

return category;
}

static CarlaBackend::PluginCategory getFromTag(const water::CharPointer_UTF8 tag)
{
if (tag.compareIgnoreCase(water::CharPointer_UTF8("synthesis")) == 0)
return CarlaBackend::PLUGIN_CATEGORY_SYNTH;

if (tag.compareIgnoreCase(water::CharPointer_UTF8("delay")) == 0)
return CarlaBackend::PLUGIN_CATEGORY_DELAY;

if (tag.compareIgnoreCase(water::CharPointer_UTF8("equalizer")) == 0)
return CarlaBackend::PLUGIN_CATEGORY_EQ;

if (tag.compareIgnoreCase(water::CharPointer_UTF8("filter")) == 0)
return CarlaBackend::PLUGIN_CATEGORY_FILTER;

if (tag.compareIgnoreCase(water::CharPointer_UTF8("distortion")) == 0)
return CarlaBackend::PLUGIN_CATEGORY_DISTORTION;

if (tag.compareIgnoreCase(water::CharPointer_UTF8("dynamics")) == 0)
return CarlaBackend::PLUGIN_CATEGORY_DYNAMICS;

if (tag.compareIgnoreCase(water::CharPointer_UTF8("modulation")) == 0)
return CarlaBackend::PLUGIN_CATEGORY_MODULATOR;

if (tag.compareIgnoreCase(water::CharPointer_UTF8("utility")) == 0)
return CarlaBackend::PLUGIN_CATEGORY_UTILITY;

return CarlaBackend::PLUGIN_CATEGORY_NONE;
}
};

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

class CarlaJsfxState
{
public:
static water::String convertToString(ysfx_state_t &state)
{
water::XmlElement root("JSFXState");

for (uint32_t i = 0; i < state.slider_count; ++i)
{
water::XmlElement *slider = new water::XmlElement("Slider");
slider->setAttribute("index", (int32_t)state.sliders[i].index);
slider->setAttribute("value", state.sliders[i].value);
root.addChildElement(slider);
}

{
const CarlaString base64 = CarlaString::asBase64(state.data, state.data_size);
water::XmlElement *var = new water::XmlElement("Serialization");
var->addTextElement(base64.buffer());
root.addChildElement(var);
}

water::MemoryOutputStream stream;
root.writeToStream(stream, water::StringRef(), true);
return stream.toUTF8();
}

static ysfx_state_u convertFromString(const water::String& string)
{
std::unique_ptr<water::XmlElement> root(water::XmlDocument::parse(string));

CARLA_SAFE_ASSERT_RETURN(root != nullptr, nullptr);
CARLA_SAFE_ASSERT_RETURN(root->getTagName() == "JSFXState", nullptr);

std::vector<ysfx_state_slider_t> sliders;
std::vector<uint8_t> serialization;

sliders.reserve(ysfx_max_sliders);

int numChildren = root->getNumChildElements();
for (int i = 0; i < numChildren; ++i)
{
water::XmlElement* child = root->getChildElement(i);
CARLA_SAFE_ASSERT_CONTINUE(child != nullptr);

if (child->getTagName() == "Slider")
{
CARLA_SAFE_ASSERT_CONTINUE(child->hasAttribute("index"));
CARLA_SAFE_ASSERT_CONTINUE(child->hasAttribute("value"));

ysfx_state_slider_t item;
int32_t index = child->getIntAttribute("index");
CARLA_SAFE_ASSERT_CONTINUE(index >= 0 && index < ysfx_max_sliders);

item.index = (uint32_t)index;
item.value = child->getDoubleAttribute("value");
sliders.push_back(item);
}
else if (child->getTagName() == "Serialization")
{
serialization = carla_getChunkFromBase64String(child->getAllSubText().toRawUTF8());
}
else
{
CARLA_SAFE_ASSERT_CONTINUE(false);
}
}

ysfx_state_t state;
state.sliders = sliders.data();
state.slider_count = (uint32_t)sliders.size();
state.data = serialization.data();
state.data_size = (uint32_t)serialization.size();

return ysfx_state_u(ysfx_state_dup(&state));
}
};

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

class CarlaJsfxUnit
{
public:
CarlaJsfxUnit() = default;

CarlaJsfxUnit(const water::File& rootPath, const water::File& filePath)
: fRootPath(rootPath), fFileId(filePath.getRelativePathFrom(rootPath))
{
#ifdef CARLA_OS_WIN
fFileId.replaceCharacter('\\', '/');
#endif
}

explicit operator bool() const
{
return fFileId.isNotEmpty();
}

const water::File& getRootPath() const
{
return fRootPath;
}

const water::String& getFileId() const
{
return fFileId;
}

water::File getFilePath() const
{
return fRootPath.getChildFile(fFileId);
}

private:
water::File fRootPath;
water::String fFileId;
};

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

#endif // CARLA_JSFX_UTILS_HPP_INCLUDED

Loading…
Cancel
Save