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