| @@ -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 | |||