Browse Source

Merge pull request #1529 from jpcima/jsfx-2

jsfx: cleaned up implementation
tags/v2.5.0
Filipe Coelho GitHub 3 years ago
parent
commit
e8fae2c811
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
100 changed files with 32450 additions and 23 deletions
  1. +4
    -2
      .github/workflows/build.yml
  2. +2
    -0
      Makefile
  3. +3
    -2
      data/carla-single
  4. +7
    -0
      resources/ui/carla_database.ui
  5. +7
    -0
      resources/ui/carla_refresh.ui
  6. +27
    -0
      resources/ui/carla_settings.ui
  7. +8
    -0
      source/Makefile.deps.mk
  8. +1
    -0
      source/backend/CarlaPlugin.hpp
  9. +4
    -0
      source/backend/Makefile
  10. +6
    -4
      source/backend/engine/CarlaEngine.cpp
  11. +1072
    -0
      source/backend/plugin/CarlaPluginJSFX.cpp
  12. +6
    -0
      source/backend/plugin/Makefile
  13. +117
    -0
      source/backend/utils/CachedPlugins.cpp
  14. +5
    -0
      source/backend/utils/Information.cpp
  15. +4
    -0
      source/backend/utils/Makefile
  16. +9
    -0
      source/bridges-plugin/Makefile
  17. +5
    -0
      source/discovery/Makefile
  18. +69
    -0
      source/discovery/carla-discovery.cpp
  19. +1
    -0
      source/frontend/C++/carla_database.cpp
  20. +2
    -0
      source/frontend/C++/carla_host.cpp
  21. +46
    -1
      source/frontend/C++/carla_settings.cpp
  22. +4
    -0
      source/frontend/C++/carla_shared.hpp
  23. +80
    -13
      source/frontend/carla_database.py
  24. +2
    -0
      source/frontend/carla_host.py
  25. +40
    -1
      source/frontend/carla_settings.py
  26. +16
    -0
      source/frontend/carla_shared.py
  27. +5
    -0
      source/frontend/carla_utils.py
  28. +13
    -0
      source/modules/ysfx/COPYING
  29. +202
    -0
      source/modules/ysfx/LICENSE-2.0.txt
  30. +169
    -0
      source/modules/ysfx/Makefile
  31. +484
    -0
      source/modules/ysfx/include/ysfx.h
  32. +139
    -0
      source/modules/ysfx/sources/base64/Base64.hpp
  33. +20
    -0
      source/modules/ysfx/sources/eel2-gas/LICENSE.txt
  34. +1399
    -0
      source/modules/ysfx/sources/eel2-gas/sources/asm-nseel-x64-sse.S
  35. +1
    -0
      source/modules/ysfx/sources/eel2-gas/sources/ref-hash-sha512.txt
  36. +62
    -0
      source/modules/ysfx/sources/lice_stb/lice_stb_bmp.cpp
  37. +76
    -0
      source/modules/ysfx/sources/lice_stb/lice_stb_generic.hpp
  38. +67
    -0
      source/modules/ysfx/sources/lice_stb/lice_stb_gif.cpp
  39. +62
    -0
      source/modules/ysfx/sources/lice_stb/lice_stb_jpg.cpp
  40. +26
    -0
      source/modules/ysfx/sources/lice_stb/lice_stb_loaders.cpp
  41. +23
    -0
      source/modules/ysfx/sources/lice_stb/lice_stb_loaders.hpp
  42. +62
    -0
      source/modules/ysfx/sources/lice_stb/lice_stb_png.cpp
  43. +101
    -0
      source/modules/ysfx/sources/lice_stb/lice_stb_write.cpp
  44. +169
    -0
      source/modules/ysfx/sources/utility/sync_bitset.hpp
  45. +1540
    -0
      source/modules/ysfx/sources/ysfx.cpp
  46. +192
    -0
      source/modules/ysfx/sources/ysfx.hpp
  47. +144
    -0
      source/modules/ysfx/sources/ysfx_api_eel.cpp
  48. +57
    -0
      source/modules/ysfx/sources/ysfx_api_eel.hpp
  49. +574
    -0
      source/modules/ysfx/sources/ysfx_api_file.cpp
  50. +127
    -0
      source/modules/ysfx/sources/ysfx_api_file.hpp
  51. +470
    -0
      source/modules/ysfx/sources/ysfx_api_gfx.cpp
  52. +50
    -0
      source/modules/ysfx/sources/ysfx_api_gfx.hpp
  53. +91
    -0
      source/modules/ysfx/sources/ysfx_api_gfx_dummy.hpp
  54. +1490
    -0
      source/modules/ysfx/sources/ysfx_api_gfx_lice.hpp
  55. +439
    -0
      source/modules/ysfx/sources/ysfx_api_reaper.cpp
  56. +20
    -0
      source/modules/ysfx/sources/ysfx_api_reaper.hpp
  57. +166
    -0
      source/modules/ysfx/sources/ysfx_audio_flac.cpp
  58. +21
    -0
      source/modules/ysfx/sources/ysfx_audio_flac.hpp
  59. +162
    -0
      source/modules/ysfx/sources/ysfx_audio_wav.cpp
  60. +21
    -0
      source/modules/ysfx/sources/ysfx_audio_wav.hpp
  61. +159
    -0
      source/modules/ysfx/sources/ysfx_config.cpp
  62. +39
    -0
      source/modules/ysfx/sources/ysfx_config.hpp
  63. +66
    -0
      source/modules/ysfx/sources/ysfx_eel_utils.cpp
  64. +57
    -0
      source/modules/ysfx/sources/ysfx_eel_utils.hpp
  65. +214
    -0
      source/modules/ysfx/sources/ysfx_midi.cpp
  66. +72
    -0
      source/modules/ysfx/sources/ysfx_midi.hpp
  67. +380
    -0
      source/modules/ysfx/sources/ysfx_parse.cpp
  68. +101
    -0
      source/modules/ysfx/sources/ysfx_parse.hpp
  69. +171
    -0
      source/modules/ysfx/sources/ysfx_preset.cpp
  70. +18
    -0
      source/modules/ysfx/sources/ysfx_preset.hpp
  71. +98
    -0
      source/modules/ysfx/sources/ysfx_reader.cpp
  72. +56
    -0
      source/modules/ysfx/sources/ysfx_reader.hpp
  73. +730
    -0
      source/modules/ysfx/sources/ysfx_utils.cpp
  74. +185
    -0
      source/modules/ysfx/sources/ysfx_utils.hpp
  75. +60
    -0
      source/modules/ysfx/sources/ysfx_utils_fts.cpp
  76. +19
    -0
      source/modules/ysfx/thirdparty/WDL/LICENSE.txt
  77. +482
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/assocarray.h
  78. +248
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/denormal.h
  79. +329
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/dirscan.h
  80. +1270
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-aarch64-gcc.c
  81. +1308
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-arm-gcc.c
  82. +1436
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-ppc-gcc.c
  83. +1357
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-x64-sse.asm
  84. +1332
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-x86-gcc.c
  85. +3017
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-x86-msvc.c
  86. +113
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel2.l
  87. +376
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel2.y
  88. +71
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_atomic.h
  89. +81
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_eval.h
  90. +396
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_fft.h
  91. +262
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_files.h
  92. +104
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_import.h
  93. +3055
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_lice.h
  94. +782
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_mdct.h
  95. +73
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_misc.h
  96. +564
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_net.h
  97. +1668
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_strings.h
  98. +776
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eelscript.h
  99. +399
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_aarch64.h
  100. +335
    -0
      source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_arm.h

+ 4
- 2
.github/workflows/build.yml View File

@@ -65,11 +65,12 @@ jobs:
sudo dpkg --add-architecture i386 sudo dpkg --add-architecture i386
sudo apt-get update -qq sudo apt-get update -qq
sudo apt-get install -yq libasound2-dev libfluidsynth-dev libgl1-mesa-dev liblo-dev libmagic-dev libpulse-dev libqt4-dev libsndfile1-dev libx11-dev libxcursor-dev libxext-dev libxrandr-dev pkg-config pyqt5-dev-tools qtbase5-dev sudo apt-get install -yq libasound2-dev libfluidsynth-dev libgl1-mesa-dev liblo-dev libmagic-dev libpulse-dev libqt4-dev libsndfile1-dev libx11-dev libxcursor-dev libxext-dev libxrandr-dev pkg-config pyqt5-dev-tools qtbase5-dev
sudo apt-get install -yq g++-multilib libfreetype6:i386 libx11-6:i386 libxext6:i386
sudo apt-get install -yq g++-multilib libfreetype6:i386 libfontconfig1:i386 libx11-6:i386 libxext6:i386
# Fix 32bit bridge build # Fix 32bit bridge build
sudo ln -s /usr/lib/i386-linux-gnu/libX11.so.6 /usr/lib/i386-linux-gnu/libX11.so sudo ln -s /usr/lib/i386-linux-gnu/libX11.so.6 /usr/lib/i386-linux-gnu/libX11.so
sudo ln -s /usr/lib/i386-linux-gnu/libXext.so.6 /usr/lib/i386-linux-gnu/libXext.so sudo ln -s /usr/lib/i386-linux-gnu/libXext.so.6 /usr/lib/i386-linux-gnu/libXext.so
sudo ln -s /usr/lib/i386-linux-gnu/libfreetype.so.6 /usr/lib/i386-linux-gnu/libfreetype.so sudo ln -s /usr/lib/i386-linux-gnu/libfreetype.so.6 /usr/lib/i386-linux-gnu/libfreetype.so
sudo ln -s /usr/lib/i386-linux-gnu/libfontconfig.so.1 /usr/lib/i386-linux-gnu/libfontconfig.so
- name: make features - name: make features
run: make features run: make features
- name: make - name: make
@@ -91,11 +92,12 @@ jobs:
sudo dpkg --add-architecture i386 sudo dpkg --add-architecture i386
sudo apt-get update -qq sudo apt-get update -qq
sudo apt-get install -yq libasound2-dev libfluidsynth-dev libgl1-mesa-dev liblo-dev libmagic-dev libpulse-dev libsndfile1-dev libx11-dev libxcursor-dev libxext-dev libxrandr-dev pkg-config pyqt5-dev-tools qtbase5-dev sudo apt-get install -yq libasound2-dev libfluidsynth-dev libgl1-mesa-dev liblo-dev libmagic-dev libpulse-dev libsndfile1-dev libx11-dev libxcursor-dev libxext-dev libxrandr-dev pkg-config pyqt5-dev-tools qtbase5-dev
sudo apt-get install -yq g++-multilib libfreetype6:i386 libx11-6:i386 libxext6:i386
sudo apt-get install -yq g++-multilib libfreetype6:i386 libfontconfig1:i386 libx11-6:i386 libxext6:i386
# Fix 32bit bridge build # Fix 32bit bridge build
sudo ln -s /usr/lib/i386-linux-gnu/libX11.so.6 /usr/lib/i386-linux-gnu/libX11.so sudo ln -s /usr/lib/i386-linux-gnu/libX11.so.6 /usr/lib/i386-linux-gnu/libX11.so
sudo ln -s /usr/lib/i386-linux-gnu/libXext.so.6 /usr/lib/i386-linux-gnu/libXext.so sudo ln -s /usr/lib/i386-linux-gnu/libXext.so.6 /usr/lib/i386-linux-gnu/libXext.so
sudo ln -s /usr/lib/i386-linux-gnu/libfreetype.so.6 /usr/lib/i386-linux-gnu/libfreetype.so sudo ln -s /usr/lib/i386-linux-gnu/libfreetype.so.6 /usr/lib/i386-linux-gnu/libfreetype.so
sudo ln -s /usr/lib/i386-linux-gnu/libfontconfig.so.1 /usr/lib/i386-linux-gnu/libfontconfig.so
- name: make features - name: make features
run: make features run: make features
- name: make - name: make


+ 2
- 0
Makefile View File

@@ -88,6 +88,8 @@ ifeq ($(USING_RTAUDIO),true)
3RD_LIBS += $(MODULEDIR)/rtmidi.a 3RD_LIBS += $(MODULEDIR)/rtmidi.a
endif endif


3RD_LIBS += $(MODULEDIR)/ysfx.a

ALL_LIBS += $(3RD_LIBS) ALL_LIBS += $(3RD_LIBS)


3rd: $(3RD_LIBS) 3rd: $(3RD_LIBS)


+ 3
- 2
data/carla-single View File

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


@@ -97,7 +98,7 @@ if len(sys.argv) == arg:
# -------------------------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------------------------
# Set format # 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] FORMAT = sys.argv[arg]
arg += 1 arg += 1


@@ -193,7 +194,7 @@ if ARCH not in ("native", "posix32", "posix64", "win32", "win64"):
print("Invalid arch") print("Invalid arch")
sys.exit(1) 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") print("Invalid format")
sys.exit(1) sys.exit(1)




+ 7
- 0
resources/ui/carla_database.ui View File

@@ -692,6 +692,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="ch_jsfx">
<property name="text">
<string>JSFX</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QCheckBox" name="ch_kits"> <widget class="QCheckBox" name="ch_kits">
<property name="text"> <property name="text">


+ 7
- 0
resources/ui/carla_refresh.ui View File

@@ -85,6 +85,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="ch_jsfx">
<property name="text">
<string>JSFX</string>
</property>
</widget>
</item>
<item> <item>
<widget class="Line" name="line_3"> <widget class="Line" name="line_3">
<property name="lineWidth"> <property name="lineWidth">


+ 27
- 0
resources/ui/carla_settings.ui View File

@@ -2154,6 +2154,11 @@
<string>SFZ</string> <string>SFZ</string>
</property> </property>
</item> </item>
<item>
<property name="text">
<string>JSFX</string>
</property>
</item>
</widget> </widget>
</item> </item>
<item> <item>
@@ -2358,6 +2363,28 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tw_paths_jsfx">
<layout class="QVBoxLayout" name="verticalLayout_24">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QListWidget" name="lw_jsfx"/>
</item>
</layout>
</widget>
</widget> </widget>
</item> </item>
<item> <item>


+ 8
- 0
source/Makefile.deps.mk View File

@@ -509,6 +509,14 @@ JUCE_GUI_BASICS_LIBS = -lgdi32 -limm32 -lcomdlg32 -lole32
endif # USING_JUCE endif # USING_JUCE
endif # WIN32 endif # WIN32


YSFX_FLAGS = -I$(CWD)/modules/ysfx/include
ifeq ($(MACOS),true)
YSFX_GRAPHICS_LIBS = -framework Cocoa -framework Carbon -framework Metal -framework Foundation
else ifneq ($(WIN32),true)
YSFX_GRAPHICS_FLAGS = $(shell $(PKG_CONFIG) $(PKG_CONFIG_FLAGS) --cflags freetype2 fontconfig)
YSFX_GRAPHICS_LIBS = $(shell $(PKG_CONFIG) $(PKG_CONFIG_FLAGS) --libs freetype2 fontconfig)
endif

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


ifeq ($(STATIC_PLUGIN_TARGET),true) ifeq ($(STATIC_PLUGIN_TARGET),true)


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

@@ -978,6 +978,7 @@ public:
static CarlaPluginPtr newVST2(const Initializer& init); static CarlaPluginPtr newVST2(const Initializer& init);
static CarlaPluginPtr newVST3(const Initializer& init); static CarlaPluginPtr newVST3(const Initializer& init);
static CarlaPluginPtr newAU(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 newJuce(const Initializer& init, const char* format);
static CarlaPluginPtr newFluidSynth(const Initializer& init, PluginType ptype, bool use16Outs); static CarlaPluginPtr newFluidSynth(const Initializer& init, PluginType ptype, bool use16Outs);


+ 4
- 0
source/backend/Makefile View File

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


STANDALONE_LIBS += $(MODULEDIR)/ysfx.a

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


STANDALONE_LINK_FLAGS = $(HYLIA_LIBS) STANDALONE_LINK_FLAGS = $(HYLIA_LIBS)
@@ -97,6 +99,8 @@ STANDALONE_LINK_FLAGS += $(RTAUDIO_LIBS)
STANDALONE_LINK_FLAGS += $(RTMIDI_LIBS) STANDALONE_LINK_FLAGS += $(RTMIDI_LIBS)
endif endif


STANDALONE_LINK_FLAGS += $(YSFX_GRAPHICS_LIBS)

ifeq ($(JACKBRIDGE_DIRECT),true) ifeq ($(JACKBRIDGE_DIRECT),true)
STANDALONE_LINK_FLAGS += $(JACK_LIBS) STANDALONE_LINK_FLAGS += $(JACK_LIBS)
endif 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_GIG
&& ptype != PLUGIN_SF2 && ptype != PLUGIN_SF2
&& ptype != PLUGIN_SFZ && ptype != PLUGIN_SFZ
&& ptype != PLUGIN_JSFX
&& ptype != PLUGIN_JACK; && ptype != PLUGIN_JACK;


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


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

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


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

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

@@ -34,6 +34,7 @@ OBJS = \
$(OBJDIR)/CarlaPluginVST2.cpp.o \ $(OBJDIR)/CarlaPluginVST2.cpp.o \
$(OBJDIR)/CarlaPluginVST3.cpp.o \ $(OBJDIR)/CarlaPluginVST3.cpp.o \
$(OBJDIR)/CarlaPluginAU.cpp.o \ $(OBJDIR)/CarlaPluginAU.cpp.o \
$(OBJDIR)/CarlaPluginJSFX.cpp.o \
$(OBJDIR)/CarlaPluginJuce.cpp.o \ $(OBJDIR)/CarlaPluginJuce.cpp.o \
$(OBJDIR)/CarlaPluginFluidSynth.cpp.o \ $(OBJDIR)/CarlaPluginFluidSynth.cpp.o \
$(OBJDIR)/CarlaPluginSFZero.cpp.o $(OBJDIR)/CarlaPluginSFZero.cpp.o
@@ -72,6 +73,11 @@ $(OBJDIR)/CarlaPluginFluidSynth.cpp.o: CarlaPluginFluidSynth.cpp
@echo "Compiling $<" @echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(FLUIDSYNTH_FLAGS) -c -o $@ $(SILENT)$(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) ifeq ($(MACOS),true)
$(OBJDIR)/CarlaPluginVST2.cpp.o: CarlaPluginVST2.cpp $(OBJDIR)/CarlaPluginVST2.cpp.o: CarlaPluginVST2.cpp
-@mkdir -p $(OBJDIR) -@mkdir -p $(OBJDIR)


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

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


#if defined(USING_JUCE) && defined(CARLA_OS_MAC) #if defined(USING_JUCE) && defined(CARLA_OS_MAC)
# include "AppConfig.h" # include "AppConfig.h"
@@ -96,6 +97,37 @@ static void findSFZs(const char* const sfzPaths)
} }
} }
} }
// -------------------------------------------------------------------------------------------------------------------

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


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


@@ -630,6 +662,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) uint carla_get_cached_plugin_count(CB::PluginType ptype, const char* pluginPath)
{ {
CARLA_SAFE_ASSERT_RETURN(isCachedPluginType(ptype), 0); CARLA_SAFE_ASSERT_RETURN(isCachedPluginType(ptype), 0);
@@ -661,6 +768,11 @@ uint carla_get_cached_plugin_count(CB::PluginType ptype, const char* pluginPath)
return static_cast<uint>(gSFZs.size()); return static_cast<uint>(gSFZs.size());
} }


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

default: default:
return 0; return 0;
} }
@@ -706,6 +818,11 @@ const CarlaCachedPluginInfo* carla_get_cached_plugin_info(CB::PluginType ptype,
return get_cached_plugin_sfz(gSFZs[index]); return get_cached_plugin_sfz(gSFZs[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: default:
break; break;
} }


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

@@ -76,6 +76,8 @@ const char* carla_get_complete_license_text()
"<li>AU plugin support (using JUCE)</li>" "<li>AU plugin support (using JUCE)</li>"
#endif #endif


"<li>JSFX plugin support (using ysfx)</li>"

// Sample kit libraries // Sample kit libraries
#if defined(HAVE_FLUIDSYNTH) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH) #if defined(HAVE_FLUIDSYNTH) && !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
"<li>FluidSynth library v" FLUIDSYNTH_VERSION " for SF2/3 support</li>" "<li>FluidSynth library v" FLUIDSYNTH_VERSION " for SF2/3 support</li>"
@@ -185,6 +187,9 @@ const char* const* carla_get_supported_file_extensions()
// SFZ // SFZ
"sfz", "sfz",


// JSFX
"jsfx",

// terminator // terminator
nullptr nullptr
}; };


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

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


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


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


@@ -62,6 +63,9 @@ LINK_FLAGS += $(JUCE_GUI_BASICS_LIBS)
LINK_FLAGS += $(JUCE_GUI_EXTRA_LIBS) LINK_FLAGS += $(JUCE_GUI_EXTRA_LIBS)
endif endif


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

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


all: $(TARGETS) all: $(TARGETS)


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

@@ -147,6 +147,9 @@ LIBS_win64 += $(MODULEDIR)/juce_gui_extra.win64.a
LINK_FLAGS += $(JUCE_GUI_EXTRA_LIBS) LINK_FLAGS += $(JUCE_GUI_EXTRA_LIBS)
endif endif


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

ifeq ($(JACKBRIDGE_DIRECT),true) ifeq ($(JACKBRIDGE_DIRECT),true)
LINK_FLAGS += $(JACK_LIBS) LINK_FLAGS += $(JACK_LIBS)
endif endif
@@ -205,6 +208,7 @@ OBJS_native = \
$(OBJDIR)/CarlaPluginVST2.cpp.o \ $(OBJDIR)/CarlaPluginVST2.cpp.o \
$(OBJDIR)/CarlaPluginVST3.cpp.o \ $(OBJDIR)/CarlaPluginVST3.cpp.o \
$(OBJDIR)/CarlaPluginAU.cpp.o \ $(OBJDIR)/CarlaPluginAU.cpp.o \
$(OBJDIR)/CarlaPluginJSFX.cpp.o \
$(OBJDIR)/CarlaPluginJuce.cpp.o \ $(OBJDIR)/CarlaPluginJuce.cpp.o \
$(OBJDIR)/CarlaPluginFluidSynth.cpp.o \ $(OBJDIR)/CarlaPluginFluidSynth.cpp.o \
$(OBJDIR)/CarlaPluginSFZero.cpp.o \ $(OBJDIR)/CarlaPluginSFZero.cpp.o \
@@ -306,6 +310,11 @@ $(BINDIR)/$(MODULENAME)-win64.exe: $(OBJS_win64) $(LIBS_win64)
# --------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------
# native # 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) ifeq ($(MACOS),true)
$(OBJDIR)/CarlaPluginVST2.cpp.o: $(CWD)/backend/plugin/CarlaPluginVST2.cpp $(OBJDIR)/CarlaPluginVST2.cpp.o: $(CWD)/backend/plugin/CarlaPluginVST2.cpp
-@mkdir -p $(OBJDIR) -@mkdir -p $(OBJDIR)


+ 5
- 0
source/discovery/Makefile View File

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


NATIVE_BUILD_FLAGS += $(YSFX_FLAGS)

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


LIBS_native = $(MODULEDIR)/lilv.a LIBS_native = $(MODULEDIR)/lilv.a
@@ -152,6 +154,9 @@ LIBS_win64 += $(MODULEDIR)/juce_gui_extra.win64.a
LINK_FLAGS += $(JUCE_GUI_EXTRA_LIBS) LINK_FLAGS += $(JUCE_GUI_EXTRA_LIBS)
endif # USING_JUCE endif # USING_JUCE


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

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


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


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

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


#include "CarlaMIDI.h" #include "CarlaMIDI.h"
#include "LinkedList.hpp" #include "LinkedList.hpp"
@@ -54,6 +55,7 @@


#ifndef BUILD_BRIDGE #ifndef BUILD_BRIDGE
# include "CarlaDssiUtils.cpp" # include "CarlaDssiUtils.cpp"
# include "CarlaJsfxUtils.hpp"
# include "../backend/utils/CachedPlugins.cpp" # include "../backend/utils/CachedPlugins.cpp"
#endif #endif


@@ -2112,6 +2114,67 @@ static void do_fluidsynth_check(const char* const filename, const PluginType typ
#endif #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 ------------------------------ // ------------------------------ main entry point ------------------------------


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


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

case PLUGIN_DLS: case PLUGIN_DLS:
case PLUGIN_GIG: case PLUGIN_GIG:
case PLUGIN_SF2: case PLUGIN_SF2:


+ 1
- 0
source/frontend/C++/carla_database.cpp View File

@@ -209,6 +209,7 @@ PluginRefreshW::PluginRefreshW(QWidget* const parent, const CarlaHost& host)
connect(self->ui.ch_au, SIGNAL(clicked()), SLOT(slot_checkTools())); connect(self->ui.ch_au, SIGNAL(clicked()), SLOT(slot_checkTools()));
connect(self->ui.ch_sf2, SIGNAL(clicked()), SLOT(slot_checkTools())); connect(self->ui.ch_sf2, SIGNAL(clicked()), SLOT(slot_checkTools()));
connect(self->ui.ch_sfz, SIGNAL(clicked()), SLOT(slot_checkTools())); connect(self->ui.ch_sfz, SIGNAL(clicked()), SLOT(slot_checkTools()));
connect(self->ui.ch_jsfx, SIGNAL(clicked()), SLOT(slot_checkTools()));
connect(&self->fThread, SIGNAL(pluginLook(float, QString)), SLOT(slot_handlePluginLook(float, QString))); connect(&self->fThread, SIGNAL(pluginLook(float, QString)), SLOT(slot_handlePluginLook(float, QString)));
connect(&self->fThread, SIGNAL(finished(int)), SLOT(slot_handlePluginThreadFinished())); connect(&self->fThread, SIGNAL(finished(int)), SLOT(slot_handlePluginThreadFinished()));




+ 2
- 0
source/frontend/C++/carla_host.cpp View File

@@ -2676,6 +2676,7 @@ QString setEngineSettings(CarlaHost& host)
QStringList VST3_PATH = settings.valueStringList(CARLA_KEY_PATHS_VST3, CARLA_DEFAULT_VST3_PATH); QStringList VST3_PATH = settings.valueStringList(CARLA_KEY_PATHS_VST3, CARLA_DEFAULT_VST3_PATH);
QStringList SF2_PATH = settings.valueStringList(CARLA_KEY_PATHS_SF2, CARLA_DEFAULT_SF2_PATH); QStringList SF2_PATH = settings.valueStringList(CARLA_KEY_PATHS_SF2, CARLA_DEFAULT_SF2_PATH);
QStringList SFZ_PATH = settings.valueStringList(CARLA_KEY_PATHS_SFZ, CARLA_DEFAULT_SFZ_PATH); QStringList SFZ_PATH = settings.valueStringList(CARLA_KEY_PATHS_SFZ, CARLA_DEFAULT_SFZ_PATH);
QStringList JSFX_PATH = settings.valueStringList(CARLA_KEY_PATHS_JSFX, CARLA_DEFAULT_JSFX_PATH);


/* /*
// TODO // TODO
@@ -2686,6 +2687,7 @@ QString setEngineSettings(CarlaHost& host)
carla_set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_VST3, splitter.join(VST3_PATH)) carla_set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_VST3, splitter.join(VST3_PATH))
carla_set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_SF2, splitter.join(SF2_PATH)) carla_set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_SF2, splitter.join(SF2_PATH))
carla_set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_SFZ, splitter.join(SFZ_PATH)) carla_set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_SFZ, splitter.join(SFZ_PATH))
carla_set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_JSFX, splitter.join(JSFX_PATH))
*/ */


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


+ 46
- 1
source/frontend/C++/carla_settings.cpp View File

@@ -426,7 +426,8 @@ enum PluginPathIndexes {
PLUGINPATH_INDEX_VST2, PLUGINPATH_INDEX_VST2,
PLUGINPATH_INDEX_VST3, PLUGINPATH_INDEX_VST3,
PLUGINPATH_INDEX_SF2, PLUGINPATH_INDEX_SF2,
PLUGINPATH_INDEX_SFZ
PLUGINPATH_INDEX_SFZ,
PLUGINPATH_INDEX_JSFX
}; };


/* /*
@@ -662,6 +663,7 @@ struct CarlaSettingsW::PrivateData {
QStringList vst3s = settings.valueStringList(CARLA_KEY_PATHS_VST3, CARLA_DEFAULT_VST3_PATH); QStringList vst3s = settings.valueStringList(CARLA_KEY_PATHS_VST3, CARLA_DEFAULT_VST3_PATH);
QStringList sf2s = settings.valueStringList(CARLA_KEY_PATHS_SF2, CARLA_DEFAULT_SF2_PATH); QStringList sf2s = settings.valueStringList(CARLA_KEY_PATHS_SF2, CARLA_DEFAULT_SF2_PATH);
QStringList sfzs = settings.valueStringList(CARLA_KEY_PATHS_SFZ, CARLA_DEFAULT_SFZ_PATH); QStringList sfzs = settings.valueStringList(CARLA_KEY_PATHS_SFZ, CARLA_DEFAULT_SFZ_PATH);
QStringList jsfxs = settings.valueStringList(CARLA_KEY_PATHS_JSFX, CARLA_DEFAULT_JSFX_PATH);


ladspas.sort(); ladspas.sort();
dssis.sort(); dssis.sort();
@@ -670,6 +672,7 @@ struct CarlaSettingsW::PrivateData {
vst3s.sort(); vst3s.sort();
sf2s.sort(); sf2s.sort();
sfzs.sort(); sfzs.sort();
jsfxs.sort();


for (const QString& ladspa : ladspas) for (const QString& ladspa : ladspas)
{ {
@@ -713,6 +716,12 @@ struct CarlaSettingsW::PrivateData {
ui.lw_sfz->addItem(sfz); ui.lw_sfz->addItem(sfz);
} }


for (const QString& jsfx : jsfxs)
{
if (jsfx.isEmpty()) continue;
ui.lw_jsfx->addItem(jsfx);
}

// ------------------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------------------
// Wine // Wine


@@ -883,6 +892,7 @@ struct CarlaSettingsW::PrivateData {
QStringList vst3s; QStringList vst3s;
QStringList sf2s; QStringList sf2s;
QStringList sfzs; QStringList sfzs;
QStringList jsfxs;


for (int i=0; i < ui.lw_ladspa->count(); ++i) for (int i=0; i < ui.lw_ladspa->count(); ++i)
ladspas.append(ui.lw_ladspa->item(i)->text()); ladspas.append(ui.lw_ladspa->item(i)->text());
@@ -905,6 +915,9 @@ struct CarlaSettingsW::PrivateData {
for (int i=0; i < ui.lw_sfz->count(); ++i) for (int i=0; i < ui.lw_sfz->count(); ++i)
sfzs.append(ui.lw_sfz->item(i)->text()); sfzs.append(ui.lw_sfz->item(i)->text());


for (int i=0; i < ui.lw_jsfx->count(); ++i)
jsfxs.append(ui.lw_jsfx->item(i)->text());

/* TODO /* TODO
host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_LADSPA, splitter.join(ladspas)); host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_LADSPA, splitter.join(ladspas));
host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_DSSI, splitter.join(dssis)); host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_DSSI, splitter.join(dssis));
@@ -913,6 +926,7 @@ struct CarlaSettingsW::PrivateData {
host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_VST3, splitter.join(vst3s)); host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_VST3, splitter.join(vst3s));
host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_SF2, splitter.join(sf2s)); host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_SF2, splitter.join(sf2s));
host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_SFZ, splitter.join(sfzs)); host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_SFZ, splitter.join(sfzs));
host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_JSFX, splitter.join(jsfxs));
*/ */


settings.setValue(CARLA_KEY_PATHS_LADSPA, ladspas); settings.setValue(CARLA_KEY_PATHS_LADSPA, ladspas);
@@ -922,6 +936,7 @@ struct CarlaSettingsW::PrivateData {
settings.setValue(CARLA_KEY_PATHS_VST3, vst3s); settings.setValue(CARLA_KEY_PATHS_VST3, vst3s);
settings.setValue(CARLA_KEY_PATHS_SF2, sf2s); settings.setValue(CARLA_KEY_PATHS_SF2, sf2s);
settings.setValue(CARLA_KEY_PATHS_SFZ, sfzs); settings.setValue(CARLA_KEY_PATHS_SFZ, sfzs);
settings.setValue(CARLA_KEY_PATHS_JSFX, jsfxs);


// ------------------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------------------
// Wine // Wine
@@ -1147,6 +1162,19 @@ struct CarlaSettingsW::PrivateData {
ui.lw_sfz->addItem(path); ui.lw_sfz->addItem(path);
} }
break; break;

case PLUGINPATH_INDEX_JSFX:
paths = CARLA_DEFAULT_JSFX_PATH;
paths.sort();
ui.lw_jsfx->clear();

for (const auto& path : paths)
{
if (path.isEmpty())
continue;
ui.lw_jsfx->addItem(path);
}
break;
} }
break; break;
} }
@@ -1310,6 +1338,7 @@ CarlaSettingsW::CarlaSettingsW(QWidget* const parent, CarlaHost& host, const boo
connect(self->ui.lw_vst3, SIGNAL(currentRowChanged(int)), SLOT(slot_pluginPathRowChanged(int))); connect(self->ui.lw_vst3, SIGNAL(currentRowChanged(int)), SLOT(slot_pluginPathRowChanged(int)));
connect(self->ui.lw_sf2, SIGNAL(currentRowChanged(int)), SLOT(slot_pluginPathRowChanged(int))); connect(self->ui.lw_sf2, SIGNAL(currentRowChanged(int)), SLOT(slot_pluginPathRowChanged(int)));
connect(self->ui.lw_sfz, SIGNAL(currentRowChanged(int)), SLOT(slot_pluginPathRowChanged(int))); connect(self->ui.lw_sfz, SIGNAL(currentRowChanged(int)), SLOT(slot_pluginPathRowChanged(int)));
connect(self->ui.lw_jsfx, SIGNAL(currentRowChanged(int)), SLOT(slot_pluginPathRowChanged(int)));


connect(self->ui.b_filepaths_add, SIGNAL(clicked()), SLOT(slot_addFilePath())); connect(self->ui.b_filepaths_add, SIGNAL(clicked()), SLOT(slot_addFilePath()));
connect(self->ui.b_filepaths_remove, SIGNAL(clicked()), SLOT(slot_removeFilePath())); connect(self->ui.b_filepaths_remove, SIGNAL(clicked()), SLOT(slot_removeFilePath()));
@@ -1335,6 +1364,7 @@ CarlaSettingsW::CarlaSettingsW(QWidget* const parent, CarlaHost& host, const boo
self->ui.lw_vst3->setCurrentRow(0); self->ui.lw_vst3->setCurrentRow(0);
self->ui.lw_sf2->setCurrentRow(0); self->ui.lw_sf2->setCurrentRow(0);
self->ui.lw_sfz->setCurrentRow(0); self->ui.lw_sfz->setCurrentRow(0);
self->ui.lw_jsfx->setCurrentRow(0);


self->ui.lw_files_audio->setCurrentRow(0); self->ui.lw_files_audio->setCurrentRow(0);
self->ui.lw_files_midi->setCurrentRow(0); self->ui.lw_files_midi->setCurrentRow(0);
@@ -1485,6 +1515,9 @@ void CarlaSettingsW::slot_addPluginPath()
case PLUGINPATH_INDEX_SFZ: case PLUGINPATH_INDEX_SFZ:
self->ui.lw_sfz->addItem(newPath); self->ui.lw_sfz->addItem(newPath);
break; break;
case PLUGINPATH_INDEX_JSFX:
self->ui.lw_jsfx->addItem(newPath);
break;
} }
} }


@@ -1513,6 +1546,9 @@ void CarlaSettingsW::slot_removePluginPath()
case PLUGINPATH_INDEX_SFZ: case PLUGINPATH_INDEX_SFZ:
self->ui.lw_sfz->takeItem(self->ui.lw_sfz->currentRow()); self->ui.lw_sfz->takeItem(self->ui.lw_sfz->currentRow());
break; break;
case PLUGINPATH_INDEX_JSFX:
self->ui.lw_jsfx->takeItem(self->ui.lw_jsfx->currentRow());
break;
} }
} }


@@ -1545,6 +1581,9 @@ void CarlaSettingsW::slot_changePluginPath()
case PLUGINPATH_INDEX_SFZ: case PLUGINPATH_INDEX_SFZ:
currentPath = self->ui.lw_sfz->currentItem()->text(); currentPath = self->ui.lw_sfz->currentItem()->text();
break; break;
case PLUGINPATH_INDEX_JSFX:
currentPath = self->ui.lw_jsfx->currentItem()->text();
break;
} }


const QString newPath = QFileDialog::getExistingDirectory(this, tr("Add Path"), currentPath, QFileDialog::ShowDirsOnly); const QString newPath = QFileDialog::getExistingDirectory(this, tr("Add Path"), currentPath, QFileDialog::ShowDirsOnly);
@@ -1575,6 +1614,9 @@ void CarlaSettingsW::slot_changePluginPath()
case PLUGINPATH_INDEX_SFZ: case PLUGINPATH_INDEX_SFZ:
self->ui.lw_sfz->currentItem()->setText(newPath); self->ui.lw_sfz->currentItem()->setText(newPath);
break; break;
case PLUGINPATH_INDEX_JSFX:
self->ui.lw_jsfx->currentItem()->setText(newPath);
break;
} }
} }


@@ -1607,6 +1649,9 @@ void CarlaSettingsW::slot_pluginPathTabChanged(const int index)
case PLUGINPATH_INDEX_SFZ: case PLUGINPATH_INDEX_SFZ:
row = self->ui.lw_sfz->currentRow(); row = self->ui.lw_sfz->currentRow();
break; break;
case PLUGINPATH_INDEX_JSFX:
row = self->ui.lw_jsfx->currentRow();
break;
default: default:
row = -1; row = -1;
break; break;


+ 4
- 0
source/frontend/C++/carla_shared.hpp View File

@@ -172,6 +172,7 @@ static const char* const* const MIDI_CC_LIST = {
#define CARLA_KEY_PATHS_VST3 "Paths/VST3" #define CARLA_KEY_PATHS_VST3 "Paths/VST3"
#define CARLA_KEY_PATHS_SF2 "Paths/SF2" #define CARLA_KEY_PATHS_SF2 "Paths/SF2"
#define CARLA_KEY_PATHS_SFZ "Paths/SFZ" #define CARLA_KEY_PATHS_SFZ "Paths/SFZ"
#define CARLA_KEY_PATHS_JSFX "Paths/JSFX"


#define CARLA_KEY_WINE_EXECUTABLE "Wine/Executable" /* str */ #define CARLA_KEY_WINE_EXECUTABLE "Wine/Executable" /* str */
#define CARLA_KEY_WINE_AUTO_PREFIX "Wine/AutoPrefix" /* bool */ #define CARLA_KEY_WINE_AUTO_PREFIX "Wine/AutoPrefix" /* bool */
@@ -299,6 +300,7 @@ static const char* const* const MIDI_CC_LIST = {
#define DEFAULT_VST3_PATH "" #define DEFAULT_VST3_PATH ""
#define DEFAULT_SF2_PATH "" #define DEFAULT_SF2_PATH ""
#define DEFAULT_SFZ_PATH "" #define DEFAULT_SFZ_PATH ""
#define DEFAULT_JSFX_PATH ""


#ifdef CARLA_OS_WIN #ifdef CARLA_OS_WIN
# define CARLA_PATH_SPLITTER ";" # define CARLA_PATH_SPLITTER ";"
@@ -338,6 +340,7 @@ if WINDOWS:
# define CARLA_DEFAULT_VST3_PATH = std::getenv("VST3_PATH", DEFAULT_VST3_PATH).split(CARLA_PATH_SPLITTER) # define CARLA_DEFAULT_VST3_PATH = std::getenv("VST3_PATH", DEFAULT_VST3_PATH).split(CARLA_PATH_SPLITTER)
# define CARLA_DEFAULT_SF2_PATH = std::getenv("SF2_PATH", DEFAULT_SF2_PATH).split(CARLA_PATH_SPLITTER) # define CARLA_DEFAULT_SF2_PATH = std::getenv("SF2_PATH", DEFAULT_SF2_PATH).split(CARLA_PATH_SPLITTER)
# define CARLA_DEFAULT_SFZ_PATH = std::getenv("SFZ_PATH", DEFAULT_SFZ_PATH).split(CARLA_PATH_SPLITTER) # define CARLA_DEFAULT_SFZ_PATH = std::getenv("SFZ_PATH", DEFAULT_SFZ_PATH).split(CARLA_PATH_SPLITTER)
# define CARLA_DEFAULT_JSFX_PATH = std::getenv("JSFX_PATH", DEFAULT_JSFX_PATH).split(CARLA_PATH_SPLITTER)
#else #else
*/ */
# define CARLA_DEFAULT_LADSPA_PATH QString(DEFAULT_LADSPA_PATH).split(CARLA_PATH_SPLITTER) # define CARLA_DEFAULT_LADSPA_PATH QString(DEFAULT_LADSPA_PATH).split(CARLA_PATH_SPLITTER)
@@ -347,6 +350,7 @@ if WINDOWS:
# define CARLA_DEFAULT_VST3_PATH QString(DEFAULT_VST3_PATH).split(CARLA_PATH_SPLITTER) # define CARLA_DEFAULT_VST3_PATH QString(DEFAULT_VST3_PATH).split(CARLA_PATH_SPLITTER)
# define CARLA_DEFAULT_SF2_PATH QString(DEFAULT_SF2_PATH).split(CARLA_PATH_SPLITTER) # define CARLA_DEFAULT_SF2_PATH QString(DEFAULT_SF2_PATH).split(CARLA_PATH_SPLITTER)
# define CARLA_DEFAULT_SFZ_PATH QString(DEFAULT_SFZ_PATH).split(CARLA_PATH_SPLITTER) # define CARLA_DEFAULT_SFZ_PATH QString(DEFAULT_SFZ_PATH).split(CARLA_PATH_SPLITTER)
# define CARLA_DEFAULT_JSFX_PATH QString(DEFAULT_JSFX_PATH).split(CARLA_PATH_SPLITTER)
/* /*
#endif #endif
*/ */


+ 80
- 13
source/frontend/carla_database.py View File

@@ -410,6 +410,7 @@ class SearchPluginsThread(QThread):
self.fCheckAU = False self.fCheckAU = False
self.fCheckSF2 = False self.fCheckSF2 = False
self.fCheckSFZ = False self.fCheckSFZ = False
self.fCheckJSFX = False


if WINDOWS: if WINDOWS:
toolNative = "carla-discovery-native.exe" toolNative = "carla-discovery-native.exe"
@@ -447,7 +448,7 @@ class SearchPluginsThread(QThread):
self.fCheckWin32 = win32 self.fCheckWin32 = win32
self.fCheckWin64 = win64 self.fCheckWin64 = win64


def setSearchPluginTypes(self, ladspa, dssi, lv2, vst2, vst3, au, sf2, sfz):
def setSearchPluginTypes(self, ladspa, dssi, lv2, vst2, vst3, au, sf2, sfz, jsfx):
self.fCheckLADSPA = ladspa self.fCheckLADSPA = ladspa
self.fCheckDSSI = dssi self.fCheckDSSI = dssi
self.fCheckLV2 = lv2 self.fCheckLV2 = lv2
@@ -456,6 +457,7 @@ class SearchPluginsThread(QThread):
self.fCheckAU = au and MACOS self.fCheckAU = au and MACOS
self.fCheckSF2 = sf2 self.fCheckSF2 = sf2
self.fCheckSFZ = sfz self.fCheckSFZ = sfz
self.fCheckJSFX = jsfx


def stop(self): def stop(self):
self.fContinueChecking = False self.fContinueChecking = False
@@ -522,6 +524,12 @@ class SearchPluginsThread(QThread):
else: else:
self.fCheckSF2 = False self.fCheckSF2 = False


if self.fCheckJSFX:
if self.fCheckNative:
self.fCurCount += 1
else:
self.fCheckJSFX = False

if self.fCurCount == 0: if self.fCurCount == 0:
return return


@@ -721,6 +729,11 @@ class SearchPluginsThread(QThread):
settingsDB.setValue("Plugins/SFZ", kits) settingsDB.setValue("Plugins/SFZ", kits)
settingsDB.sync() settingsDB.sync()


if self.fCheckJSFX:
kits = self._checkJsfxCached()
settingsDB.setValue("Plugins/JSFX", kits)
settingsDB.sync()

def _checkLADSPA(self, OS, tool, isWine=False): def _checkLADSPA(self, OS, tool, isWine=False):
ladspaBinaries = [] ladspaBinaries = []
ladspaPlugins = [] ladspaPlugins = []
@@ -994,6 +1007,36 @@ class SearchPluginsThread(QThread):
self.fLastCheckValue += self.fCurPercentValue self.fLastCheckValue += self.fCurPercentValue
return sfzKits return sfzKits


def _checkJsfxCached(self):
settings = QSafeSettings("falkTX", "Carla2")
PLUG_PATH = splitter.join(settings.value(CARLA_KEY_PATHS_JSFX, CARLA_DEFAULT_JSFX_PATH, list))
del settings

jsfxPlugins = []
self._pluginLook(self.fLastCheckValue, "JSFX plugins...")

count = gCarla.utils.get_cached_plugin_count(PLUGIN_JSFX, PLUG_PATH)

if not self.fContinueChecking:
return jsfxPlugins

for i in range(count):
descInfo = gCarla.utils.get_cached_plugin_info(PLUGIN_JSFX, i)

percent = ( float(i) / count ) * self.fCurPercentValue
self._pluginLook(self.fLastCheckValue + percent, descInfo['label'])

if not descInfo['valid']:
continue

jsfxPlugins.append(checkPluginCached(descInfo, PLUGIN_JSFX))

if not self.fContinueChecking:
break

self.fLastCheckValue += self.fCurPercentValue
return jsfxPlugins

def _pluginLook(self, percent, plugin): def _pluginLook(self, percent, plugin):
self.pluginLook.emit(percent, plugin) self.pluginLook.emit(percent, plugin)


@@ -1227,6 +1270,7 @@ class PluginRefreshW(QDialog):
self.ui.ch_au.clicked.connect(self.slot_checkTools) self.ui.ch_au.clicked.connect(self.slot_checkTools)
self.ui.ch_sf2.clicked.connect(self.slot_checkTools) self.ui.ch_sf2.clicked.connect(self.slot_checkTools)
self.ui.ch_sfz.clicked.connect(self.slot_checkTools) self.ui.ch_sfz.clicked.connect(self.slot_checkTools)
self.ui.ch_jsfx.clicked.connect(self.slot_checkTools)
self.fThread.pluginLook.connect(self.slot_handlePluginLook) self.fThread.pluginLook.connect(self.slot_handlePluginLook)
self.fThread.finished.connect(self.slot_handlePluginThreadFinished) self.fThread.finished.connect(self.slot_handlePluginThreadFinished)


@@ -1267,6 +1311,9 @@ class PluginRefreshW(QDialog):
check = settings.value("PluginDatabase/SearchSFZ", False, bool) and self.ui.ch_sfz.isEnabled() check = settings.value("PluginDatabase/SearchSFZ", False, bool) and self.ui.ch_sfz.isEnabled()
self.ui.ch_sfz.setChecked(check) self.ui.ch_sfz.setChecked(check)


check = settings.value("PluginDatabase/SearchJSFX", True, bool) and self.ui.ch_jsfx.isEnabled()
self.ui.ch_jsfx.setChecked(check)

check = settings.value("PluginDatabase/SearchNative", True, bool) and self.ui.ch_native.isEnabled() check = settings.value("PluginDatabase/SearchNative", True, bool) and self.ui.ch_native.isEnabled()
self.ui.ch_native.setChecked(check) self.ui.ch_native.setChecked(check)


@@ -1297,6 +1344,7 @@ class PluginRefreshW(QDialog):
settings.setValue("PluginDatabase/SearchAU", self.ui.ch_au.isChecked()) settings.setValue("PluginDatabase/SearchAU", self.ui.ch_au.isChecked())
settings.setValue("PluginDatabase/SearchSF2", self.ui.ch_sf2.isChecked()) settings.setValue("PluginDatabase/SearchSF2", self.ui.ch_sf2.isChecked())
settings.setValue("PluginDatabase/SearchSFZ", self.ui.ch_sfz.isChecked()) settings.setValue("PluginDatabase/SearchSFZ", self.ui.ch_sfz.isChecked())
settings.setValue("PluginDatabase/SearchJSFX", self.ui.ch_jsfx.isChecked())
settings.setValue("PluginDatabase/SearchNative", self.ui.ch_native.isChecked()) settings.setValue("PluginDatabase/SearchNative", self.ui.ch_native.isChecked())
settings.setValue("PluginDatabase/SearchPOSIX32", self.ui.ch_posix32.isChecked()) settings.setValue("PluginDatabase/SearchPOSIX32", self.ui.ch_posix32.isChecked())
settings.setValue("PluginDatabase/SearchPOSIX64", self.ui.ch_posix64.isChecked()) settings.setValue("PluginDatabase/SearchPOSIX64", self.ui.ch_posix64.isChecked())
@@ -1326,13 +1374,14 @@ class PluginRefreshW(QDialog):
self.ui.ch_posix32.isChecked(), self.ui.ch_posix64.isChecked(), self.ui.ch_posix32.isChecked(), self.ui.ch_posix64.isChecked(),
self.ui.ch_win32.isChecked(), self.ui.ch_win64.isChecked()) self.ui.ch_win32.isChecked(), self.ui.ch_win64.isChecked())


ladspa, dssi, lv2, vst, vst3, au, sf2, sfz = (self.ui.ch_ladspa.isChecked(), self.ui.ch_dssi.isChecked(),
self.ui.ch_lv2.isChecked(), self.ui.ch_vst.isChecked(),
self.ui.ch_vst3.isChecked(), self.ui.ch_au.isChecked(),
self.ui.ch_sf2.isChecked(), self.ui.ch_sfz.isChecked())
ladspa, dssi, lv2, vst, vst3, au, sf2, sfz, jsfx = (self.ui.ch_ladspa.isChecked(), self.ui.ch_dssi.isChecked(),
self.ui.ch_lv2.isChecked(), self.ui.ch_vst.isChecked(),
self.ui.ch_vst3.isChecked(), self.ui.ch_au.isChecked(),
self.ui.ch_sf2.isChecked(), self.ui.ch_sfz.isChecked(),
self.ui.ch_jsfx.isChecked())


self.fThread.setSearchBinaryTypes(native, posix32, posix64, win32, win64) self.fThread.setSearchBinaryTypes(native, posix32, posix64, win32, win64)
self.fThread.setSearchPluginTypes(ladspa, dssi, lv2, vst, vst3, au, sf2, sfz)
self.fThread.setSearchPluginTypes(ladspa, dssi, lv2, vst, vst3, au, sf2, sfz, jsfx)
self.fThread.start() self.fThread.start()


# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
@@ -1352,7 +1401,8 @@ class PluginRefreshW(QDialog):
enabled2 = bool(self.ui.ch_ladspa.isChecked() or self.ui.ch_dssi.isChecked() or enabled2 = bool(self.ui.ch_ladspa.isChecked() or self.ui.ch_dssi.isChecked() or
self.ui.ch_lv2.isChecked() or self.ui.ch_vst.isChecked() or self.ui.ch_lv2.isChecked() or self.ui.ch_vst.isChecked() or
self.ui.ch_vst3.isChecked() or self.ui.ch_au.isChecked() or self.ui.ch_vst3.isChecked() or self.ui.ch_au.isChecked() or
self.ui.ch_sf2.isChecked() or self.ui.ch_sfz.isChecked())
self.ui.ch_sf2.isChecked() or self.ui.ch_sfz.isChecked() or
self.ui.ch_jsfx.isChecked())


self.ui.b_start.setEnabled(enabled1 and enabled2) self.ui.b_start.setEnabled(enabled1 and enabled2)


@@ -1512,6 +1562,7 @@ class PluginDatabaseW(QDialog):
self.ui.ch_vst.clicked.connect(self.slot_checkFilters) self.ui.ch_vst.clicked.connect(self.slot_checkFilters)
self.ui.ch_vst3.clicked.connect(self.slot_checkFilters) self.ui.ch_vst3.clicked.connect(self.slot_checkFilters)
self.ui.ch_au.clicked.connect(self.slot_checkFilters) self.ui.ch_au.clicked.connect(self.slot_checkFilters)
self.ui.ch_jsfx.clicked.connect(self.slot_checkFilters)
self.ui.ch_kits.clicked.connect(self.slot_checkFilters) self.ui.ch_kits.clicked.connect(self.slot_checkFilters)
self.ui.ch_effects.clicked.connect(self.slot_checkFilters) self.ui.ch_effects.clicked.connect(self.slot_checkFilters)
self.ui.ch_instruments.clicked.connect(self.slot_checkFilters) self.ui.ch_instruments.clicked.connect(self.slot_checkFilters)
@@ -1704,6 +1755,7 @@ class PluginDatabaseW(QDialog):
self.ui.ch_dssi.setChecked(True) self.ui.ch_dssi.setChecked(True)
self.ui.ch_lv2.setChecked(True) self.ui.ch_lv2.setChecked(True)
self.ui.ch_vst.setChecked(True) self.ui.ch_vst.setChecked(True)
self.ui.ch_jsfx.setChecked(True)
self.ui.ch_kits.setChecked(True) self.ui.ch_kits.setChecked(True)


self.ui.ch_instruments.setChecked(True) self.ui.ch_instruments.setChecked(True)
@@ -1762,6 +1814,7 @@ class PluginDatabaseW(QDialog):
settings.setValue("PluginDatabase/ShowVST2", self.ui.ch_vst.isChecked()) settings.setValue("PluginDatabase/ShowVST2", self.ui.ch_vst.isChecked())
settings.setValue("PluginDatabase/ShowVST3", self.ui.ch_vst3.isChecked()) settings.setValue("PluginDatabase/ShowVST3", self.ui.ch_vst3.isChecked())
settings.setValue("PluginDatabase/ShowAU", self.ui.ch_au.isChecked()) settings.setValue("PluginDatabase/ShowAU", self.ui.ch_au.isChecked())
settings.setValue("PluginDatabase/ShowJSFX", self.ui.ch_jsfx.isChecked())
settings.setValue("PluginDatabase/ShowKits", self.ui.ch_kits.isChecked()) settings.setValue("PluginDatabase/ShowKits", self.ui.ch_kits.isChecked())
settings.setValue("PluginDatabase/ShowNative", self.ui.ch_native.isChecked()) settings.setValue("PluginDatabase/ShowNative", self.ui.ch_native.isChecked())
settings.setValue("PluginDatabase/ShowBridged", self.ui.ch_bridged.isChecked()) settings.setValue("PluginDatabase/ShowBridged", self.ui.ch_bridged.isChecked())
@@ -1822,6 +1875,7 @@ class PluginDatabaseW(QDialog):
self.ui.ch_vst.setChecked(settings.value("PluginDatabase/ShowVST2", True, bool)) self.ui.ch_vst.setChecked(settings.value("PluginDatabase/ShowVST2", True, bool))
self.ui.ch_vst3.setChecked(settings.value("PluginDatabase/ShowVST3", (MACOS or WINDOWS), bool)) self.ui.ch_vst3.setChecked(settings.value("PluginDatabase/ShowVST3", (MACOS or WINDOWS), bool))
self.ui.ch_au.setChecked(settings.value("PluginDatabase/ShowAU", MACOS, bool)) self.ui.ch_au.setChecked(settings.value("PluginDatabase/ShowAU", MACOS, bool))
self.ui.ch_jsfx.setChecked(settings.value("PluginDatabase/ShowJSFX", True, bool))
self.ui.ch_kits.setChecked(settings.value("PluginDatabase/ShowKits", True, bool)) self.ui.ch_kits.setChecked(settings.value("PluginDatabase/ShowKits", True, bool))
self.ui.ch_native.setChecked(settings.value("PluginDatabase/ShowNative", True, bool)) self.ui.ch_native.setChecked(settings.value("PluginDatabase/ShowNative", True, bool))
self.ui.ch_bridged.setChecked(settings.value("PluginDatabase/ShowBridged", True, bool)) self.ui.ch_bridged.setChecked(settings.value("PluginDatabase/ShowBridged", True, bool))
@@ -1897,6 +1951,7 @@ class PluginDatabaseW(QDialog):
hideVST2 = not self.ui.ch_vst.isChecked() hideVST2 = not self.ui.ch_vst.isChecked()
hideVST3 = not self.ui.ch_vst3.isChecked() hideVST3 = not self.ui.ch_vst3.isChecked()
hideAU = not self.ui.ch_au.isChecked() hideAU = not self.ui.ch_au.isChecked()
hideJSFX = not self.ui.ch_jsfx.isChecked()
hideKits = not self.ui.ch_kits.isChecked() hideKits = not self.ui.ch_kits.isChecked()


hideNative = not self.ui.ch_native.isChecked() hideNative = not self.ui.ch_native.isChecked()
@@ -1973,6 +2028,8 @@ class PluginDatabaseW(QDialog):
self.ui.tableWidget.hideRow(i) self.ui.tableWidget.hideRow(i)
elif hideAU and ptype == PLUGIN_AU: elif hideAU and ptype == PLUGIN_AU:
self.ui.tableWidget.hideRow(i) self.ui.tableWidget.hideRow(i)
elif hideJSFX and ptype == PLUGIN_JSFX:
self.ui.tableWidget.hideRow(i)
elif hideNative and isNative: elif hideNative and isNative:
self.ui.tableWidget.hideRow(i) self.ui.tableWidget.hideRow(i)
elif hideBridged and isBridged: elif hideBridged and isBridged:
@@ -2012,7 +2069,7 @@ class PluginDatabaseW(QDialog):
def _addPluginToTable(self, plugin, ptype): def _addPluginToTable(self, plugin, ptype):
if plugin['API'] != PLUGIN_QUERY_API_VERSION: if plugin['API'] != PLUGIN_QUERY_API_VERSION:
return return
if ptype in (self.tr("Internal"), "LV2", "SF2", "SFZ"):
if ptype in (self.tr("Internal"), "LV2", "SF2", "SFZ", "JSFX"):
plugin['build'] = BINARY_NATIVE plugin['build'] = BINARY_NATIVE


index = self.fLastTableIndex index = self.fLastTableIndex
@@ -2048,6 +2105,7 @@ class PluginDatabaseW(QDialog):
#elif ptype == PLUGIN_SFZ: #elif ptype == PLUGIN_SFZ:
#ptypeStr = "SFZ" #ptypeStr = "SFZ"
#ptypeStrTr = ptypeStr #ptypeStrTr = ptypeStr
# TODO(jsfx) what to do here?
else: else:
return 0 return 0


@@ -2159,6 +2217,11 @@ class PluginDatabaseW(QDialog):


auPlugins32 = settingsDB.value("Plugins/AU_posix32", [], list) if MACOS else [] auPlugins32 = settingsDB.value("Plugins/AU_posix32", [], list) if MACOS else []


# ----------------------------------------------------------------------------------------------------
# JSFX

jsfxPlugins = settingsDB.value("Plugins/JSFX", [], list)

# ---------------------------------------------------------------------------------------------------- # ----------------------------------------------------------------------------------------------------
# Kits # Kits


@@ -2173,6 +2236,7 @@ class PluginDatabaseW(QDialog):
vstCount = 0 vstCount = 0
vst3Count = 0 vst3Count = 0
au32Count = 0 au32Count = 0
jsfxCount = len(jsfxPlugins)
sf2Count = 0 sf2Count = 0
sfzCount = len(sfzs) sfzCount = len(sfzs)


@@ -2195,15 +2259,15 @@ class PluginDatabaseW(QDialog):
sf2Count += len(plugins) sf2Count += len(plugins)


self.ui.tableWidget.setRowCount(self.fLastTableIndex + self.ui.tableWidget.setRowCount(self.fLastTableIndex +
ladspaCount + dssiCount + vstCount + vst3Count + au32Count +
ladspaCount + dssiCount + vstCount + vst3Count + au32Count + jsfxCount +
sf2Count + sfzCount) sf2Count + sfzCount)


if MACOS: if MACOS:
self.ui.label.setText(self.tr("Have %i Internal, %i LADSPA, %i DSSI, %i LV2, %i VST2, %i VST3 and %i AudioUnit plugins, plus %i Sound Kits" % (
internalCount, ladspaCount, dssiCount, lv2Count, vstCount, vst3Count, auCount+au32Count, sf2Count+sfzCount)))
self.ui.label.setText(self.tr("Have %i Internal, %i LADSPA, %i DSSI, %i LV2, %i VST2, %i VST3, %i AudioUnit plugins and %i JSFX plugins, plus %i Sound Kits" % (
internalCount, ladspaCount, dssiCount, lv2Count, vstCount, vst3Count, auCount+au32Count, jsfxCount, sf2Count+sfzCount)))
else: else:
self.ui.label.setText(self.tr("Have %i Internal, %i LADSPA, %i DSSI, %i LV2, %i VST2 and %i VST3 plugins, plus %i Sound Kits" % (
internalCount, ladspaCount, dssiCount, lv2Count, vstCount, vst3Count, sf2Count+sfzCount)))
self.ui.label.setText(self.tr("Have %i Internal, %i LADSPA, %i DSSI, %i LV2, %i VST2, %i VST3 plugins and %i JSFX plugins, plus %i Sound Kits" % (
internalCount, ladspaCount, dssiCount, lv2Count, vstCount, vst3Count, jsfxCount, sf2Count+sfzCount)))


# ---------------------------------------------------------------------------------------------------- # ----------------------------------------------------------------------------------------------------
# now add all plugins to the table # now add all plugins to the table
@@ -2228,6 +2292,9 @@ class PluginDatabaseW(QDialog):
for plugin in plugins: for plugin in plugins:
self._addPluginToTable(plugin, "AU") self._addPluginToTable(plugin, "AU")


for plugin in jsfxPlugins:
self._addPluginToTable(plugin, "JSFX")

for sf2 in sf2s: for sf2 in sf2s:
for sf2_i in sf2: for sf2_i in sf2:
self._addPluginToTable(sf2_i, "SF2") self._addPluginToTable(sf2_i, "SF2")


+ 2
- 0
source/frontend/carla_host.py View File

@@ -3393,6 +3393,7 @@ def setEngineSettings(host, oscPort = None):
VST3_PATH = settings.value(CARLA_KEY_PATHS_VST3, CARLA_DEFAULT_VST3_PATH, list) VST3_PATH = settings.value(CARLA_KEY_PATHS_VST3, CARLA_DEFAULT_VST3_PATH, list)
SF2_PATH = settings.value(CARLA_KEY_PATHS_SF2, CARLA_DEFAULT_SF2_PATH, list) SF2_PATH = settings.value(CARLA_KEY_PATHS_SF2, CARLA_DEFAULT_SF2_PATH, list)
SFZ_PATH = settings.value(CARLA_KEY_PATHS_SFZ, CARLA_DEFAULT_SFZ_PATH, list) SFZ_PATH = settings.value(CARLA_KEY_PATHS_SFZ, CARLA_DEFAULT_SFZ_PATH, list)
JSFX_PATH = settings.value(CARLA_KEY_PATHS_JSFX, CARLA_DEFAULT_JSFX_PATH, list)


host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_LADSPA, splitter.join(LADSPA_PATH)) host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_LADSPA, splitter.join(LADSPA_PATH))
host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_DSSI, splitter.join(DSSI_PATH)) host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_DSSI, splitter.join(DSSI_PATH))
@@ -3401,6 +3402,7 @@ def setEngineSettings(host, oscPort = None):
host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_VST3, splitter.join(VST3_PATH)) host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_VST3, splitter.join(VST3_PATH))
host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_SF2, splitter.join(SF2_PATH)) host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_SF2, splitter.join(SF2_PATH))
host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_SFZ, splitter.join(SFZ_PATH)) host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_SFZ, splitter.join(SFZ_PATH))
host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_JSFX, splitter.join(JSFX_PATH))


# -------------------------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------------------------
# don't continue if plugin # don't continue if plugin


+ 40
- 1
source/frontend/carla_settings.py View File

@@ -45,7 +45,8 @@ from carla_backend import (
PLUGIN_VST2, PLUGIN_VST2,
PLUGIN_VST3, PLUGIN_VST3,
PLUGIN_SF2, PLUGIN_SF2,
PLUGIN_SFZ
PLUGIN_SFZ,
PLUGIN_JSFX
) )


from carla_shared import ( from carla_shared import (
@@ -97,6 +98,7 @@ from carla_shared import (
CARLA_KEY_PATHS_VST3, CARLA_KEY_PATHS_VST3,
CARLA_KEY_PATHS_SF2, CARLA_KEY_PATHS_SF2,
CARLA_KEY_PATHS_SFZ, CARLA_KEY_PATHS_SFZ,
CARLA_KEY_PATHS_JSFX,
CARLA_KEY_WINE_EXECUTABLE, CARLA_KEY_WINE_EXECUTABLE,
CARLA_KEY_WINE_AUTO_PREFIX, CARLA_KEY_WINE_AUTO_PREFIX,
CARLA_KEY_WINE_FALLBACK_PREFIX, CARLA_KEY_WINE_FALLBACK_PREFIX,
@@ -171,6 +173,7 @@ from carla_shared import (
CARLA_DEFAULT_VST3_PATH, CARLA_DEFAULT_VST3_PATH,
CARLA_DEFAULT_SF2_PATH, CARLA_DEFAULT_SF2_PATH,
CARLA_DEFAULT_SFZ_PATH, CARLA_DEFAULT_SFZ_PATH,
CARLA_DEFAULT_JSFX_PATH,
getAndSetPath, getAndSetPath,
getIcon, getIcon,
fontMetricsHorizontalAdvance, fontMetricsHorizontalAdvance,
@@ -476,6 +479,7 @@ class CarlaSettingsW(QDialog):
PLUGINPATH_INDEX_VST3 = 4 PLUGINPATH_INDEX_VST3 = 4
PLUGINPATH_INDEX_SF2 = 5 PLUGINPATH_INDEX_SF2 = 5
PLUGINPATH_INDEX_SFZ = 6 PLUGINPATH_INDEX_SFZ = 6
PLUGINPATH_INDEX_JSFX = 7


# Single and Multiple client mode is only for JACK, # Single and Multiple client mode is only for JACK,
# but we still want to match QComboBox index to backend defines, # but we still want to match QComboBox index to backend defines,
@@ -604,6 +608,7 @@ class CarlaSettingsW(QDialog):
self.ui.lw_vst3.currentRowChanged.connect(self.slot_pluginPathRowChanged) self.ui.lw_vst3.currentRowChanged.connect(self.slot_pluginPathRowChanged)
self.ui.lw_sf2.currentRowChanged.connect(self.slot_pluginPathRowChanged) self.ui.lw_sf2.currentRowChanged.connect(self.slot_pluginPathRowChanged)
self.ui.lw_sfz.currentRowChanged.connect(self.slot_pluginPathRowChanged) self.ui.lw_sfz.currentRowChanged.connect(self.slot_pluginPathRowChanged)
self.ui.lw_jsfx.currentRowChanged.connect(self.slot_pluginPathRowChanged)


self.ui.b_filepaths_add.clicked.connect(self.slot_addFilePath) self.ui.b_filepaths_add.clicked.connect(self.slot_addFilePath)
self.ui.b_filepaths_remove.clicked.connect(self.slot_removeFilePath) self.ui.b_filepaths_remove.clicked.connect(self.slot_removeFilePath)
@@ -629,6 +634,7 @@ class CarlaSettingsW(QDialog):
self.ui.lw_vst3.setCurrentRow(0) self.ui.lw_vst3.setCurrentRow(0)
self.ui.lw_sf2.setCurrentRow(0) self.ui.lw_sf2.setCurrentRow(0)
self.ui.lw_sfz.setCurrentRow(0) self.ui.lw_sfz.setCurrentRow(0)
self.ui.lw_jsfx.setCurrentRow(0)


self.ui.lw_files_audio.setCurrentRow(0) self.ui.lw_files_audio.setCurrentRow(0)
self.ui.lw_files_midi.setCurrentRow(0) self.ui.lw_files_midi.setCurrentRow(0)
@@ -839,6 +845,7 @@ class CarlaSettingsW(QDialog):
vst3s = settings.value(CARLA_KEY_PATHS_VST3, CARLA_DEFAULT_VST3_PATH, list) vst3s = settings.value(CARLA_KEY_PATHS_VST3, CARLA_DEFAULT_VST3_PATH, list)
sf2s = settings.value(CARLA_KEY_PATHS_SF2, CARLA_DEFAULT_SF2_PATH, list) sf2s = settings.value(CARLA_KEY_PATHS_SF2, CARLA_DEFAULT_SF2_PATH, list)
sfzs = settings.value(CARLA_KEY_PATHS_SFZ, CARLA_DEFAULT_SFZ_PATH, list) sfzs = settings.value(CARLA_KEY_PATHS_SFZ, CARLA_DEFAULT_SFZ_PATH, list)
jsfxs = settings.value(CARLA_KEY_PATHS_JSFX, CARLA_DEFAULT_JSFX_PATH, list)


ladspas.sort() ladspas.sort()
dssis.sort() dssis.sort()
@@ -847,6 +854,7 @@ class CarlaSettingsW(QDialog):
vst3s.sort() vst3s.sort()
sf2s.sort() sf2s.sort()
sfzs.sort() sfzs.sort()
jsfxs.sort()


for ladspa in ladspas: for ladspa in ladspas:
if not ladspa: if not ladspa:
@@ -883,6 +891,11 @@ class CarlaSettingsW(QDialog):
continue continue
self.ui.lw_sfz.addItem(sfz) self.ui.lw_sfz.addItem(sfz)


for jsfx in jsfxs:
if not jsfx:
continue
self.ui.lw_jsfx.addItem(jsfx)

# ------------------------------------------------------------------------------------------------------------- # -------------------------------------------------------------------------------------------------------------
# Wine # Wine


@@ -1053,6 +1066,7 @@ class CarlaSettingsW(QDialog):
vst3s = [] vst3s = []
sf2s = [] sf2s = []
sfzs = [] sfzs = []
jsfxs = []


for i in range(self.ui.lw_ladspa.count()): for i in range(self.ui.lw_ladspa.count()):
ladspas.append(self.ui.lw_ladspa.item(i).text()) ladspas.append(self.ui.lw_ladspa.item(i).text())
@@ -1075,6 +1089,9 @@ class CarlaSettingsW(QDialog):
for i in range(self.ui.lw_sfz.count()): for i in range(self.ui.lw_sfz.count()):
sfzs.append(self.ui.lw_sfz.item(i).text()) sfzs.append(self.ui.lw_sfz.item(i).text())


for i in range(self.ui.lw_jsfx.count()):
jsfxs.append(self.ui.lw_jsfx.item(i).text())

self.host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_LADSPA, splitter.join(ladspas)) self.host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_LADSPA, splitter.join(ladspas))
self.host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_DSSI, splitter.join(dssis)) self.host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_DSSI, splitter.join(dssis))
self.host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_LV2, splitter.join(lv2s)) self.host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_LV2, splitter.join(lv2s))
@@ -1082,6 +1099,7 @@ class CarlaSettingsW(QDialog):
self.host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_VST3, splitter.join(vst3s)) self.host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_VST3, splitter.join(vst3s))
self.host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_SF2, splitter.join(sf2s)) self.host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_SF2, splitter.join(sf2s))
self.host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_SFZ, splitter.join(sfzs)) self.host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_SFZ, splitter.join(sfzs))
self.host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_JSFX, splitter.join(jsfxs))


settings.setValue(CARLA_KEY_PATHS_LADSPA, ladspas) settings.setValue(CARLA_KEY_PATHS_LADSPA, ladspas)
settings.setValue(CARLA_KEY_PATHS_DSSI, dssis) settings.setValue(CARLA_KEY_PATHS_DSSI, dssis)
@@ -1090,6 +1108,7 @@ class CarlaSettingsW(QDialog):
settings.setValue(CARLA_KEY_PATHS_VST3, vst3s) settings.setValue(CARLA_KEY_PATHS_VST3, vst3s)
settings.setValue(CARLA_KEY_PATHS_SF2, sf2s) settings.setValue(CARLA_KEY_PATHS_SF2, sf2s)
settings.setValue(CARLA_KEY_PATHS_SFZ, sfzs) settings.setValue(CARLA_KEY_PATHS_SFZ, sfzs)
settings.setValue(CARLA_KEY_PATHS_JSFX, jsfxs)


# ------------------------------------------------------------------------------------------------------------- # -------------------------------------------------------------------------------------------------------------
# Wine # Wine
@@ -1277,6 +1296,16 @@ class CarlaSettingsW(QDialog):
continue continue
self.ui.lw_sfz.addItem(path) self.ui.lw_sfz.addItem(path)


elif curIndex == self.PLUGINPATH_INDEX_JSFX:
paths = CARLA_DEFAULT_JSFX_PATH
paths.sort()
self.ui.lw_jsfx.clear()

for path in paths:
if not path:
continue
self.ui.lw_jsfx.addItem(path)

# ------------------------------------------------------------------------------------------------------------- # -------------------------------------------------------------------------------------------------------------
# Wine # Wine


@@ -1405,6 +1434,8 @@ class CarlaSettingsW(QDialog):
self.ui.lw_sf2.addItem(newPath) self.ui.lw_sf2.addItem(newPath)
elif curIndex == self.PLUGINPATH_INDEX_SFZ: elif curIndex == self.PLUGINPATH_INDEX_SFZ:
self.ui.lw_sfz.addItem(newPath) self.ui.lw_sfz.addItem(newPath)
elif curIndex == self.PLUGINPATH_INDEX_JSFX:
self.ui.lw_jsfx.addItem(newPath)


@pyqtSlot() @pyqtSlot()
def slot_removePluginPath(self): def slot_removePluginPath(self):
@@ -1424,6 +1455,8 @@ class CarlaSettingsW(QDialog):
self.ui.lw_sf2.takeItem(self.ui.lw_sf2.currentRow()) self.ui.lw_sf2.takeItem(self.ui.lw_sf2.currentRow())
elif curIndex == self.PLUGINPATH_INDEX_SFZ: elif curIndex == self.PLUGINPATH_INDEX_SFZ:
self.ui.lw_sfz.takeItem(self.ui.lw_sfz.currentRow()) self.ui.lw_sfz.takeItem(self.ui.lw_sfz.currentRow())
elif curIndex == self.PLUGINPATH_INDEX_JSFX:
self.ui.lw_jsfx.takeItem(self.ui.lw_jsfx.currentRow())


@pyqtSlot() @pyqtSlot()
def slot_changePluginPath(self): def slot_changePluginPath(self):
@@ -1443,6 +1476,8 @@ class CarlaSettingsW(QDialog):
currentPath = self.ui.lw_sf2.currentItem().text() currentPath = self.ui.lw_sf2.currentItem().text()
elif curIndex == self.PLUGINPATH_INDEX_SFZ: elif curIndex == self.PLUGINPATH_INDEX_SFZ:
currentPath = self.ui.lw_sfz.currentItem().text() currentPath = self.ui.lw_sfz.currentItem().text()
elif curIndex == self.PLUGINPATH_INDEX_JSFX:
currentPath = self.ui.lw_jsfx.currentItem().text()
else: else:
currentPath = "" currentPath = ""


@@ -1465,6 +1500,8 @@ class CarlaSettingsW(QDialog):
self.ui.lw_sf2.currentItem().setText(newPath) self.ui.lw_sf2.currentItem().setText(newPath)
elif curIndex == self.PLUGINPATH_INDEX_SFZ: elif curIndex == self.PLUGINPATH_INDEX_SFZ:
self.ui.lw_sfz.currentItem().setText(newPath) self.ui.lw_sfz.currentItem().setText(newPath)
elif curIndex == self.PLUGINPATH_INDEX_JSFX:
self.ui.lw_jsfx.currentItem().setText(newPath)


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


@@ -1484,6 +1521,8 @@ class CarlaSettingsW(QDialog):
row = self.ui.lw_sf2.currentRow() row = self.ui.lw_sf2.currentRow()
elif index == self.PLUGINPATH_INDEX_SFZ: elif index == self.PLUGINPATH_INDEX_SFZ:
row = self.ui.lw_sfz.currentRow() row = self.ui.lw_sfz.currentRow()
elif index == self.PLUGINPATH_INDEX_JSFX:
row = self.ui.lw_jsfx.currentRow()
else: else:
row = -1 row = -1




+ 16
- 0
source/frontend/carla_shared.py View File

@@ -233,6 +233,7 @@ CARLA_KEY_PATHS_VST2 = "Paths/VST2"
CARLA_KEY_PATHS_VST3 = "Paths/VST3" CARLA_KEY_PATHS_VST3 = "Paths/VST3"
CARLA_KEY_PATHS_SF2 = "Paths/SF2" CARLA_KEY_PATHS_SF2 = "Paths/SF2"
CARLA_KEY_PATHS_SFZ = "Paths/SFZ" CARLA_KEY_PATHS_SFZ = "Paths/SFZ"
CARLA_KEY_PATHS_JSFX = "Paths/JSFX"


CARLA_KEY_WINE_EXECUTABLE = "Wine/Executable" # str CARLA_KEY_WINE_EXECUTABLE = "Wine/Executable" # str
CARLA_KEY_WINE_AUTO_PREFIX = "Wine/AutoPrefix" # bool CARLA_KEY_WINE_AUTO_PREFIX = "Wine/AutoPrefix" # bool
@@ -352,6 +353,7 @@ DEFAULT_VST2_PATH = ""
DEFAULT_VST3_PATH = "" DEFAULT_VST3_PATH = ""
DEFAULT_SF2_PATH = "" DEFAULT_SF2_PATH = ""
DEFAULT_SFZ_PATH = "" DEFAULT_SFZ_PATH = ""
DEFAULT_JSFX_PATH = ""


if WINDOWS: if WINDOWS:
splitter = ";" splitter = ";"
@@ -388,6 +390,9 @@ if WINDOWS:
DEFAULT_VST2_PATH = PROGRAMFILES + "\\VstPlugins" DEFAULT_VST2_PATH = PROGRAMFILES + "\\VstPlugins"
DEFAULT_VST2_PATH += ";" + PROGRAMFILES + "\\Steinberg\\VstPlugins" DEFAULT_VST2_PATH += ";" + PROGRAMFILES + "\\Steinberg\\VstPlugins"


DEFAULT_JSFX_PATH = APPDATA + "\\REAPER\Effects"
#DEFAULT_JSFX_PATH += ";" + PROGRAMFILES + "\\REAPER\\InstallData\\Effects"

if kIs64bit: if kIs64bit:
DEFAULT_VST2_PATH += ";" + COMMONPROGRAMFILES + "\\VST2" DEFAULT_VST2_PATH += ";" + COMMONPROGRAMFILES + "\\VST2"


@@ -402,6 +407,7 @@ if WINDOWS:
DEFAULT_DSSI_PATH += ";" + PROGRAMFILESx86 + "\\DSSI" DEFAULT_DSSI_PATH += ";" + PROGRAMFILESx86 + "\\DSSI"
DEFAULT_VST2_PATH += ";" + PROGRAMFILESx86 + "\\VstPlugins" DEFAULT_VST2_PATH += ";" + PROGRAMFILESx86 + "\\VstPlugins"
DEFAULT_VST2_PATH += ";" + PROGRAMFILESx86 + "\\Steinberg\\VstPlugins" DEFAULT_VST2_PATH += ";" + PROGRAMFILESx86 + "\\Steinberg\\VstPlugins"
#DEFAULT_JSFX_PATH += ";" + PROGRAMFILESx86 + "\\REAPER\\InstallData\\Effects"


if COMMONPROGRAMFILESx86: if COMMONPROGRAMFILESx86:
DEFAULT_VST3_PATH += COMMONPROGRAMFILESx86 + "\\VST3" DEFAULT_VST3_PATH += COMMONPROGRAMFILESx86 + "\\VST3"
@@ -444,7 +450,12 @@ elif MACOS:
DEFAULT_VST3_PATH = HOME + "/Library/Audio/Plug-Ins/VST3" DEFAULT_VST3_PATH = HOME + "/Library/Audio/Plug-Ins/VST3"
DEFAULT_VST3_PATH += ":/Library/Audio/Plug-Ins/VST3" DEFAULT_VST3_PATH += ":/Library/Audio/Plug-Ins/VST3"


DEFAULT_JSFX_PATH = HOME + "/Library/Application Support/REAPER/Effects"
#DEFAULT_JSFX_PATH += ":/Applications/REAPER.app/Contents/InstallFiles/Effects"

else: else:
CONFIG_HOME = os.getenv("XDG_CONFIG_HOME", HOME + "/.config")

splitter = ":" splitter = ":"


DEFAULT_LADSPA_PATH = HOME + "/.ladspa" DEFAULT_LADSPA_PATH = HOME + "/.ladspa"
@@ -480,6 +491,9 @@ else:
DEFAULT_SFZ_PATH = HOME + "/.sounds/sfz" DEFAULT_SFZ_PATH = HOME + "/.sounds/sfz"
DEFAULT_SFZ_PATH += ":/usr/share/sounds/sfz" DEFAULT_SFZ_PATH += ":/usr/share/sounds/sfz"


DEFAULT_JSFX_PATH = CONFIG_HOME + "/REAPER/Effects"
#DEFAULT_JSFX_PATH += ":" + "/opt/REAPER/InstallData/Effects"

if not WINDOWS: if not WINDOWS:
winePrefix = os.getenv("WINEPREFIX") winePrefix = os.getenv("WINEPREFIX")


@@ -527,6 +541,7 @@ if readEnvVars:
CARLA_DEFAULT_VST3_PATH = os.getenv("VST3_PATH", DEFAULT_VST3_PATH).split(splitter) CARLA_DEFAULT_VST3_PATH = os.getenv("VST3_PATH", DEFAULT_VST3_PATH).split(splitter)
CARLA_DEFAULT_SF2_PATH = os.getenv("SF2_PATH", DEFAULT_SF2_PATH).split(splitter) CARLA_DEFAULT_SF2_PATH = os.getenv("SF2_PATH", DEFAULT_SF2_PATH).split(splitter)
CARLA_DEFAULT_SFZ_PATH = os.getenv("SFZ_PATH", DEFAULT_SFZ_PATH).split(splitter) CARLA_DEFAULT_SFZ_PATH = os.getenv("SFZ_PATH", DEFAULT_SFZ_PATH).split(splitter)
CARLA_DEFAULT_JSFX_PATH = os.getenv("JSFX_PATH", DEFAULT_JSFX_PATH).split(splitter)


else: else:
CARLA_DEFAULT_LADSPA_PATH = DEFAULT_LADSPA_PATH.split(splitter) CARLA_DEFAULT_LADSPA_PATH = DEFAULT_LADSPA_PATH.split(splitter)
@@ -536,6 +551,7 @@ else:
CARLA_DEFAULT_VST3_PATH = DEFAULT_VST3_PATH.split(splitter) CARLA_DEFAULT_VST3_PATH = DEFAULT_VST3_PATH.split(splitter)
CARLA_DEFAULT_SF2_PATH = DEFAULT_SF2_PATH.split(splitter) CARLA_DEFAULT_SF2_PATH = DEFAULT_SF2_PATH.split(splitter)
CARLA_DEFAULT_SFZ_PATH = DEFAULT_SFZ_PATH.split(splitter) CARLA_DEFAULT_SFZ_PATH = DEFAULT_SFZ_PATH.split(splitter)
CARLA_DEFAULT_JSFX_PATH = DEFAULT_JSFX_PATH.split(splitter)


# ------------------------------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------------------------------
# Default Plugin Folders (cleanup) # Default Plugin Folders (cleanup)


+ 5
- 0
source/frontend/carla_utils.py View File

@@ -43,6 +43,7 @@ from carla_backend import (
PLUGIN_VST2, PLUGIN_VST2,
PLUGIN_VST3, PLUGIN_VST3,
PLUGIN_AU, PLUGIN_AU,
PLUGIN_JSFX,
PLUGIN_DLS, PLUGIN_DLS,
PLUGIN_GIG, PLUGIN_GIG,
PLUGIN_SF2, PLUGIN_SF2,
@@ -85,6 +86,8 @@ def getPluginTypeAsString(ptype):
return "VST3" return "VST3"
if ptype == PLUGIN_AU: if ptype == PLUGIN_AU:
return "AU" return "AU"
if ptype == PLUGIN_JSFX:
return "JSFX"
if ptype == PLUGIN_DLS: if ptype == PLUGIN_DLS:
return "DLS" return "DLS"
if ptype == PLUGIN_GIG: if ptype == PLUGIN_GIG:
@@ -123,6 +126,8 @@ def getPluginTypeFromString(stype):
return PLUGIN_VST3 return PLUGIN_VST3
if stype in ("au", "audiounit"): if stype in ("au", "audiounit"):
return PLUGIN_AU return PLUGIN_AU
if stype == "jsfx":
return PLUGIN_JSFX
if stype == "dls": if stype == "dls":
return PLUGIN_DLS return PLUGIN_DLS
if stype == "gig": if stype == "gig":


+ 13
- 0
source/modules/ysfx/COPYING View File

@@ -0,0 +1,13 @@
Copyright 2021 Jean Pierre Cimalando

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

+ 202
- 0
source/modules/ysfx/LICENSE-2.0.txt View File

@@ -0,0 +1,202 @@

Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.

"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.

"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.

"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.

"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.

"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.

"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).

"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.

"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."

"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.

2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.

3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.

4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:

(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and

(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and

(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and

(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.

You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.

5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.

6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.

8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS

APPENDIX: How to apply the Apache License to your work.

To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright [yyyy] [name of copyright owner]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

+ 169
- 0
source/modules/ysfx/Makefile View File

@@ -0,0 +1,169 @@
#!/usr/bin/make -f
# Makefile for eel2 #
# ----------------- #
# Created by falkTX
#

CWD=../..
MODULENAME=ysfx
include ../Makefile.mk

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

BUILD_C_FLAGS += $(YSFX_FLAGS)
BUILD_C_FLAGS += $(YSFX_GRAPHICS_FLAGS)
BUILD_C_FLAGS += -Isources
BUILD_C_FLAGS += -Ithirdparty/WDL/source -Ithirdparty/dr_libs -Ithirdparty/stb
BUILD_C_FLAGS += -DYSFX_NO_STANDARD_MUTEX
BUILD_C_FLAGS += -DEELSCRIPT_NO_NET
BUILD_C_FLAGS += -DEELSCRIPT_NO_LICE
BUILD_C_FLAGS += -DWDL_FFT_REALSIZE=8
BUILD_C_FLAGS += -DWDL_LINEPARSE_ATOF=ysfx_wdl_atof
BUILD_C_FLAGS += -DNSEEL_ATOF=ysfx_wdl_atof

BUILD_CXX_FLAGS += $(YSFX_FLAGS)
BUILD_CXX_FLAGS += $(YSFX_GRAPHICS_FLAGS)
BUILD_CXX_FLAGS += -Isources
BUILD_CXX_FLAGS += -Ithirdparty/WDL/source -Ithirdparty/dr_libs -Ithirdparty/stb
BUILD_CXX_FLAGS += -DYSFX_NO_STANDARD_MUTEX
BUILD_CXX_FLAGS += -DEELSCRIPT_NO_NET
BUILD_CXX_FLAGS += -DEELSCRIPT_NO_LICE
BUILD_CXX_FLAGS += -DWDL_FFT_REALSIZE=8
BUILD_CXX_FLAGS += -DWDL_LINEPARSE_ATOF=ysfx_wdl_atof
BUILD_CXX_FLAGS += -DNSEEL_ATOF=ysfx_wdl_atof

ifneq ($(WIN32),true)
# NOTE: not compatible with MingGW, breaks win32_utf8
BUILD_C_FLAGS += -D_FILE_OFFSET_BITS=64
BUILD_CXX_FLAGS += -D_FILE_OFFSET_BITS=64
endif

ifeq ($(WIN32),true)
BUILD_C_FLAGS += -DNOMINMAX
BUILD_CXX_FLAGS += -DNOMINMAX
endif

ifneq ($(WIN32),true)
ifneq ($(MACOS),true)
BUILD_C_FLAGS += -DSWELL_LICE_GDI -DSWELL_FONTCONFIG -DSWELL_FREETYPE
BUILD_CXX_FLAGS += -DSWELL_LICE_GDI -DSWELL_FONTCONFIG -DSWELL_FREETYPE
endif
endif

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

OBJS = $(OBJDIR)/sources/ysfx.cpp.o
OBJS += $(OBJDIR)/sources/ysfx_api_eel.cpp.o
OBJS += $(OBJDIR)/sources/ysfx_api_file.cpp.o
OBJS += $(OBJDIR)/sources/ysfx_api_gfx.cpp.o
OBJS += $(OBJDIR)/sources/ysfx_api_reaper.cpp.o
OBJS += $(OBJDIR)/sources/ysfx_audio_flac.cpp.o
OBJS += $(OBJDIR)/sources/ysfx_audio_wav.cpp.o
OBJS += $(OBJDIR)/sources/ysfx_config.cpp.o
OBJS += $(OBJDIR)/sources/ysfx_eel_utils.cpp.o
OBJS += $(OBJDIR)/sources/ysfx_midi.cpp.o
OBJS += $(OBJDIR)/sources/ysfx_parse.cpp.o
OBJS += $(OBJDIR)/sources/ysfx_reader.cpp.o
OBJS += $(OBJDIR)/sources/ysfx_utils.cpp.o
OBJS += $(OBJDIR)/sources/ysfx_utils_fts.cpp.o
OBJS += $(OBJDIR)/sources/eel2-gas/sources/asm-nseel-x64-sse.S.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/eel2/nseel-caltab.c.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/eel2/nseel-cfunc.c.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/eel2/nseel-compiler.c.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/eel2/nseel-eval.c.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/eel2/nseel-lextab.c.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/eel2/nseel-ram.c.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/eel2/nseel-yylex.c.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/fft.c.o
ifeq ($(WIN32),true)
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/win32_utf8.c.o
endif
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice.cpp.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_arc.cpp.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_colorspace.cpp.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_image.cpp.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_line.cpp.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_palette.cpp.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_texgen.cpp.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_text.cpp.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_textnew.cpp.o
OBJS += $(OBJDIR)/sources/lice_stb/lice_stb_loaders.cpp.o
OBJS += $(OBJDIR)/sources/lice_stb/lice_stb_bmp.cpp.o
OBJS += $(OBJDIR)/sources/lice_stb/lice_stb_gif.cpp.o
OBJS += $(OBJDIR)/sources/lice_stb/lice_stb_jpg.cpp.o
OBJS += $(OBJDIR)/sources/lice_stb/lice_stb_png.cpp.o
OBJS += $(OBJDIR)/sources/lice_stb/lice_stb_write.cpp.o
ifneq ($(WIN32),true)
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-ini.cpp.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell.cpp.o
ifeq ($(MACOS),true)
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-appstub.mm.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-dlg.mm.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-gdi.mm.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-kb.mm.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-menu.mm.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-misc.mm.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-miscdlg.mm.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-modstub.mm.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-wnd.mm.o
else
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-appstub-generic.cpp.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-dlg-generic.cpp.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-gdi-generic.cpp.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-gdi-lice.cpp.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-generic-gdk.cpp.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-generic-headless.cpp.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-kb-generic.cpp.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-menu-generic.cpp.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-misc-generic.cpp.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-miscdlg-generic.cpp.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-modstub-generic.cpp.o
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-wnd-generic.cpp.o
endif
endif

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

all: $(MODULEDIR)/$(MODULENAME).a

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

clean:
rm -f $(OBJS) $(MODULEDIR)/$(MODULENAME)*.a

debug:
$(MAKE) DEBUG=true

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

$(MODULEDIR)/$(MODULENAME).a: $(OBJS)
-@mkdir -p $(MODULEDIR)
@echo "Creating $(MODULENAME).a"
@rm -f $@
@$(AR) crs $@ $^

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

$(OBJDIR)/%.c.o: %.c
-@mkdir -p $(dir $@)
@echo "Compiling $<"
@$(CC) $< $(BUILD_C_FLAGS) -c -o $@

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

$(OBJDIR)/%.mm.o: %.mm
-@mkdir -p $(dir $@)
@echo "Compiling $<"
@$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@

$(OBJDIR)/%.S.o: %.S
-@mkdir -p $(dir $@)
@echo "Compiling $<"
@$(CC) $< $(BUILD_ASM_FLAGS) -c -o $@

-include $(OBJS:%.o=%.d)

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

+ 484
- 0
source/modules/ysfx/include/ysfx.h View File

@@ -0,0 +1,484 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#pragma once
#if !defined(YSFX_INCLUDED_YSFX_H)
#define YSFX_INCLUDED_YSFX_H

#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#if defined(_WIN32)
# include <wchar.h>
#endif

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

#ifdef __cplusplus
extern "C" {
#endif

#if !defined(YSFX_API)
# if defined(_WIN32) && defined(YSFX_DLL_BUILD)
# define YSFX_API __declspec(dllexport)
# elif defined(__GNUC__)
# define YSFX_API __attribute__((visibility("default")))
# else
# define YSFX_API
# endif
#endif

//------------------------------------------------------------------------------
// YSFX definitions

typedef double ysfx_real;

enum {
ysfx_max_sliders = 64,
ysfx_max_channels = 64,
ysfx_max_midi_buses = 16,
ysfx_max_triggers = 10,
};

typedef enum ysfx_log_level_e {
ysfx_log_info,
ysfx_log_warning,
ysfx_log_error,
} ysfx_log_level;

typedef void (ysfx_log_reporter_t)(intptr_t userdata, ysfx_log_level level, const char *message);

//------------------------------------------------------------------------------
// YSFX configuration

typedef struct ysfx_config_s ysfx_config_t;
typedef struct ysfx_audio_format_s ysfx_audio_format_t;

// create a new configuration
YSFX_API ysfx_config_t *ysfx_config_new();
// delete a configuration
YSFX_API void ysfx_config_free(ysfx_config_t *config);
// increase the reference counter
YSFX_API void ysfx_config_add_ref(ysfx_config_t *config);
// set the path of the import root, a folder usually named "Effects"
YSFX_API void ysfx_set_import_root(ysfx_config_t *config, const char *root);
// set the path of the data root, a folder usually named "Data"
YSFX_API void ysfx_set_data_root(ysfx_config_t *config, const char *root);
// get the path of the import root, a folder usually named "Effects"
YSFX_API const char *ysfx_get_import_root(ysfx_config_t *config);
// get the path of the data root, a folder usually named "Data"
YSFX_API const char *ysfx_get_data_root(ysfx_config_t *config);
// guess the undefined root folders, based on the path to the JSFX file
YSFX_API void ysfx_guess_file_roots(ysfx_config_t *config, const char *sourcepath);
// register an audio format into the system
YSFX_API void ysfx_register_audio_format(ysfx_config_t *config, ysfx_audio_format_t *afmt);
// register the builtin audio formats (at least WAV file support)
YSFX_API void ysfx_register_builtin_audio_formats(ysfx_config_t *config);
// set the log reporting function
YSFX_API void ysfx_set_log_reporter(ysfx_config_t *config, ysfx_log_reporter_t *reporter);
// set the callback user data
YSFX_API void ysfx_set_user_data(ysfx_config_t *config, intptr_t userdata);

// get a string which textually represents the log level
YSFX_API const char *ysfx_log_level_string(ysfx_log_level level);

//------------------------------------------------------------------------------
// YSFX effect

typedef struct ysfx_s ysfx_t;

// create a new effect, taking a reference to config
YSFX_API ysfx_t *ysfx_new(ysfx_config_t *config);
// delete an effect
YSFX_API void ysfx_free(ysfx_t *fx);
// increase the reference counter
YSFX_API void ysfx_add_ref(ysfx_t *fx);

// get the configuration
YSFX_API ysfx_config_t *ysfx_get_config(ysfx_t *fx);

typedef enum ysfx_load_option_e {
// skip imports; useful just for accessing header information and nothing else
ysfx_load_ignoring_imports = 1,
} ysfx_load_option_t;

// load the source code from file without compiling
YSFX_API bool ysfx_load_file(ysfx_t *fx, const char *filepath, uint32_t loadopts);
// unload the source code and any compiled code
YSFX_API void ysfx_unload(ysfx_t *fx);
// check whether the effect is loaded
YSFX_API bool ysfx_is_loaded(ysfx_t *fx);

// get the name of the effect
YSFX_API const char *ysfx_get_name(ysfx_t *fx);
// get the path of the file which is loaded
YSFX_API const char *ysfx_get_file_path(ysfx_t *fx);
// get the author of the effect
YSFX_API const char *ysfx_get_author(ysfx_t *fx);
// get the number of tags of the effect
#define ysfx_get_num_tags(fx) ysfx_get_tags((fx), NULL, 0)
// get the list of tags of the effect
YSFX_API uint32_t ysfx_get_tags(ysfx_t *fx, const char **dest, uint32_t destsize);
// get a single tag of the effect
YSFX_API const char *ysfx_get_tag(ysfx_t *fx, uint32_t index);
// get the number of inputs
YSFX_API uint32_t ysfx_get_num_inputs(ysfx_t *fx);
// get the number of outputs
YSFX_API uint32_t ysfx_get_num_outputs(ysfx_t *fx);
// get the name of the input
YSFX_API const char *ysfx_get_input_name(ysfx_t *fx, uint32_t index);
// get the name of the output
YSFX_API const char *ysfx_get_output_name(ysfx_t *fx, uint32_t index);
// get whether this effect wants metering
YSFX_API bool ysfx_wants_meters(ysfx_t *fx);
// get requested dimensions of the graphics area; 0 means host should decide
YSFX_API bool ysfx_get_gfx_dim(ysfx_t *fx, uint32_t dim[2]);

typedef enum ysfx_section_type_e {
ysfx_section_init = 1,
ysfx_section_slider = 2,
ysfx_section_block = 3,
ysfx_section_sample = 4,
ysfx_section_gfx = 5,
ysfx_section_serialize = 6,
} ysfx_section_type_t;

// get whether the source has the given section
YSFX_API bool ysfx_has_section(ysfx_t *fx, uint32_t type);

typedef struct ysfx_slider_range_s {
ysfx_real def;
ysfx_real min;
ysfx_real max;
ysfx_real inc;
} ysfx_slider_range_t;

// determine if slider exists; call from 0 to max-1 to scan available ones
YSFX_API bool ysfx_slider_exists(ysfx_t *fx, uint32_t index);
// get the name of a slider
YSFX_API const char *ysfx_slider_get_name(ysfx_t *fx, uint32_t index);
// get the range of a slider
YSFX_API bool ysfx_slider_get_range(ysfx_t *fx, uint32_t index, ysfx_slider_range_t *range);
// get whether the slider is an enumeration
YSFX_API bool ysfx_slider_is_enum(ysfx_t *fx, uint32_t index);
// get the number of labels for the enumeration slider
#define ysfx_slider_get_enum_size(fx, index) ysfx_slider_get_enum_names((fx), (index), NULL, 0)
// get the list of labels for the enumeration slider
YSFX_API uint32_t ysfx_slider_get_enum_names(ysfx_t *fx, uint32_t index, const char **dest, uint32_t destsize);
// get a single label for the enumeration slider
YSFX_API const char *ysfx_slider_get_enum_name(ysfx_t *fx, uint32_t slider_index, uint32_t enum_index);
// get whether the slider is a path (implies enumeration)
YSFX_API bool ysfx_slider_is_path(ysfx_t *fx, uint32_t index);
// get whether the slider is initially visible
YSFX_API bool ysfx_slider_is_initially_visible(ysfx_t *fx, uint32_t index);

// get the value of the slider
YSFX_API ysfx_real ysfx_slider_get_value(ysfx_t *fx, uint32_t index);
// set the value of the slider, and call @slider later if value has changed
YSFX_API void ysfx_slider_set_value(ysfx_t *fx, uint32_t index, ysfx_real value);

typedef enum ysfx_compile_option_e {
// skip compiling the @serialize section
ysfx_compile_no_serialize = 1 << 0,
// skip compiling the @gfx section
ysfx_compile_no_gfx = 1 << 1,
} ysfx_compile_option_t;

// compile the previously loaded source
YSFX_API bool ysfx_compile(ysfx_t *fx, uint32_t compileopts);
// check whether the effect is compiled
YSFX_API bool ysfx_is_compiled(ysfx_t *fx);

// get the block size
YSFX_API uint32_t ysfx_get_block_size(ysfx_t *fx);
// get the sample rate
YSFX_API ysfx_real ysfx_get_sample_rate(ysfx_t *fx);
// update the block size; don't forget to call @init
YSFX_API void ysfx_set_block_size(ysfx_t *fx, uint32_t blocksize);
// update the sample rate; don't forget to call @init
YSFX_API void ysfx_set_sample_rate(ysfx_t *fx, ysfx_real samplerate);

// set the capacity of the MIDI buffer
YSFX_API void ysfx_set_midi_capacity(ysfx_t *fx, uint32_t capacity, bool extensible);

// activate and invoke @init
YSFX_API void ysfx_init(ysfx_t *fx);

// get the output latency
YSFX_API ysfx_real ysfx_get_pdc_delay(ysfx_t *fx);
// get the range of channels where output latency applies (end not included)
YSFX_API void ysfx_get_pdc_channels(ysfx_t *fx, uint32_t channels[2]);
// get whether the output latency applies to MIDI as well
YSFX_API bool ysfx_get_pdc_midi(ysfx_t *fx);

typedef enum ysfx_playback_state_e {
ysfx_playback_error = 0,
ysfx_playback_playing = 1,
ysfx_playback_paused = 2,
ysfx_playback_recording = 5,
ysfx_playback_recording_paused = 6,
} ysfx_playback_state_t;

typedef struct ysfx_time_info_s {
// tempo in beats/minute
ysfx_real tempo;
// state of the playback (ysfx_playback_state_t)
uint32_t playback_state;
// time position in seconds
ysfx_real time_position;
// time position in beats
ysfx_real beat_position;
// time signature in fraction form
uint32_t time_signature[2];
} ysfx_time_info_t;

// update time information; do this before processing the cycle
YSFX_API void ysfx_set_time_info(ysfx_t *fx, const ysfx_time_info_t *info);

typedef struct ysfx_midi_event_s {
// the bus number
uint32_t bus;
// the frame when it happens within the cycle
uint32_t offset;
// the size of the message
uint32_t size;
// the contents of the message
const uint8_t *data;
} ysfx_midi_event_t;

// send MIDI, it will be processed during the cycle
YSFX_API bool ysfx_send_midi(ysfx_t *fx, const ysfx_midi_event_t *event);
// receive MIDI, after having processed the cycle
YSFX_API bool ysfx_receive_midi(ysfx_t *fx, ysfx_midi_event_t *event);
// receive MIDI from a single bus (do not mix with API above, use either)
YSFX_API bool ysfx_receive_midi_from_bus(ysfx_t *fx, uint32_t bus, ysfx_midi_event_t *event);

// send a trigger, it will be processed during the cycle
YSFX_API bool ysfx_send_trigger(ysfx_t *fx, uint32_t index);

// get a bit mask of sliders whose values must be redisplayed, and clear it to zero
YSFX_API uint64_t ysfx_fetch_slider_changes(ysfx_t *fx);
// get a bit mask of sliders whose values must be automated, and clear it to zero
YSFX_API uint64_t ysfx_fetch_slider_automations(ysfx_t *fx);
// get a bit mask of sliders currently visible
YSFX_API uint64_t ysfx_get_slider_visibility(ysfx_t *fx);

// process a cycle in 32-bit float
YSFX_API void ysfx_process_float(ysfx_t *fx, const float *const *ins, float *const *outs, uint32_t num_ins, uint32_t num_outs, uint32_t num_frames);
// process a cycle in 64-bit float
YSFX_API void ysfx_process_double(ysfx_t *fx, const double *const *ins, double *const *outs, uint32_t num_ins, uint32_t num_outs, uint32_t num_frames);

typedef struct ysfx_state_slider_s {
// index of the slider
uint32_t index;
// value of the slider
ysfx_real value;
} ysfx_state_slider_t;

typedef struct ysfx_state_s {
// values of the sliders
ysfx_state_slider_t *sliders;
// number of sliders
uint32_t slider_count;
// serialized data
uint8_t *data;
// size of serialized data
size_t data_size;
} ysfx_state_t;

// load state
YSFX_API bool ysfx_load_state(ysfx_t *fx, ysfx_state_t *state);
// save current state; release this object when done
YSFX_API ysfx_state_t *ysfx_save_state(ysfx_t *fx);
// release a saved state object
YSFX_API void ysfx_state_free(ysfx_state_t *state);
// duplicate a state object
YSFX_API ysfx_state_t *ysfx_state_dup(ysfx_state_t *state);

typedef struct ysfx_preset_s {
// name of the preset
char *name;
// state of the preset
ysfx_state_t *state;
} ysfx_preset_t;

typedef struct ysfx_bank_s {
// name of the bank
char *name;
// list of presets
ysfx_preset_t *presets;
// number of programs
uint32_t preset_count;
} ysfx_bank_t;

// get the path of the RPL preset bank of the loaded JSFX, if present
YSFX_API const char *ysfx_get_bank_path(ysfx_t *fx);
// read a preset bank from RPL file
YSFX_API ysfx_bank_t *ysfx_load_bank(const char *path);
// free a preset bank
YSFX_API void ysfx_bank_free(ysfx_bank_t *bank);

// type of a function which can enumerate VM variables; returning 0 ends the search
typedef int (ysfx_enum_vars_callback_t)(const char *name, ysfx_real *var, void *userdata);
// enumerate all variables currently in the VM
YSFX_API void ysfx_enum_vars(ysfx_t *fx, ysfx_enum_vars_callback_t *callback, void *userdata);
// find a single variable in the VM
YSFX_API ysfx_real *ysfx_find_var(ysfx_t *fx, const char *name);
// read a chunk of virtual memory from the VM
YSFX_API void ysfx_read_vmem(ysfx_t *fx, uint32_t addr, ysfx_real *dest, uint32_t count);

//------------------------------------------------------------------------------
// YSFX graphics

// NOTE: all `ysfx_gfx_*` functions must be invoked from a dedicated UI thread

typedef struct ysfx_gfx_config_s {
// opaque user data passed to callbacks
void *user_data;
// the width of the frame buffer (having the scale factor applied)
uint32_t pixel_width;
// the height of the frame buffer (having the scale factor applied)
uint32_t pixel_height;
// the distance in bytes between lines; if 0, it defaults to (4*width)
// currently it is required to be a multiple of 4
uint32_t pixel_stride;
// the pixel data of the frame buffer, of size (stride*height) bytes
// the byte order in little-endian is 'BGRA', big-endian is 'ARGB'
uint8_t *pixels;
// the scale factor of the display; 1.0 or greater, 2.0 for Retina display
ysfx_real scale_factor;
// show a menu and run it synchronously; returns an item ID >= 1, or 0 if none
int32_t (*show_menu)(void *user_data, const char *menu_spec, int32_t xpos, int32_t ypos);
// change the cursor
void (*set_cursor)(void *user_data, int32_t cursor);
// if index is not -1, get the dropped file at this index (otherwise null)
// if index is -1, clear the list of dropped files, and return null
const char *(*get_drop_file)(void *user_data, int32_t index);
} ysfx_gfx_config_t;

// set up the graphics rendering
YSFX_API void ysfx_gfx_setup(ysfx_t *fx, ysfx_gfx_config_t *gc);
// get whether the current effect is requesting Retina support
YSFX_API bool ysfx_gfx_wants_retina(ysfx_t *fx);
// push a key to the input queue
YSFX_API void ysfx_gfx_add_key(ysfx_t *fx, uint32_t mods, uint32_t key, bool press);
// update mouse information; position is relative to canvas; wheel should be in steps normalized to ±1.0
YSFX_API void ysfx_gfx_update_mouse(ysfx_t *fx, uint32_t mods, int32_t xpos, int32_t ypos, uint32_t buttons, ysfx_real wheel, ysfx_real hwheel);
// invoke @gfx to paint the graphics; returns whether the framer buffer is modified
YSFX_API bool ysfx_gfx_run(ysfx_t *fx);

//------------------------------------------------------------------------------
// YSFX key map

// these key definitions match those of pugl

enum {
ysfx_mod_shift = 1 << 0,
ysfx_mod_ctrl = 1 << 1,
ysfx_mod_alt = 1 << 2,
ysfx_mod_super = 1 << 3,
};

enum {
ysfx_key_backspace = 0x08,
ysfx_key_escape = 0x1b,
ysfx_key_delete = 0x7f,

ysfx_key_f1 = 0xe000,
ysfx_key_f2,
ysfx_key_f3,
ysfx_key_f4,
ysfx_key_f5,
ysfx_key_f6,
ysfx_key_f7,
ysfx_key_f8,
ysfx_key_f9,
ysfx_key_f10,
ysfx_key_f11,
ysfx_key_f12,
ysfx_key_left,
ysfx_key_up,
ysfx_key_right,
ysfx_key_down,
ysfx_key_page_up,
ysfx_key_page_down,
ysfx_key_home,
ysfx_key_end,
ysfx_key_insert,
};

enum {
ysfx_button_left = 1 << 0,
ysfx_button_middle = 1 << 1,
ysfx_button_right = 1 << 2,
};

//------------------------------------------------------------------------------
// YSFX audio formats

typedef struct ysfx_audio_reader_s ysfx_audio_reader_t;

typedef struct ysfx_audio_file_info_s {
uint32_t channels;
ysfx_real sample_rate;
} ysfx_audio_file_info_t;

typedef struct ysfx_audio_format_s {
// quickly checks if this format would be able to handle the given file
bool (*can_handle)(const char *path);
// open an audio file of this format for reading
ysfx_audio_reader_t *(*open)(const char *path);
// close the audio file
void (*close)(ysfx_audio_reader_t *reader);
// get the sample rate and the channel count
ysfx_audio_file_info_t (*info)(ysfx_audio_reader_t *reader);
// get the number of samples left to read
uint64_t (*avail)(ysfx_audio_reader_t *reader);
// move the read pointer back to the beginning
void (*rewind)(ysfx_audio_reader_t *reader);
// read the next block of samples
uint64_t (*read)(ysfx_audio_reader_t *reader, ysfx_real *samples, uint64_t count);
} ysfx_audio_format_t;

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

#ifdef __cplusplus
} // extern "C"
#endif

//------------------------------------------------------------------------------
// YSFX RAII helpers

#if defined(__cplusplus) && (__cplusplus >= 201103L || (defined(_MSC_VER) && _MSVC_LANG >= 201103L))
#include <memory>

#define YSFX_DEFINE_AUTO_PTR(aptr, styp, freefn) \
struct aptr##_deleter { \
void operator()(styp *x) const noexcept { freefn(x); } \
}; \
using aptr = std::unique_ptr<styp, aptr##_deleter>

YSFX_DEFINE_AUTO_PTR(ysfx_config_u, ysfx_config_t, ysfx_config_free);
YSFX_DEFINE_AUTO_PTR(ysfx_u, ysfx_t, ysfx_free);
YSFX_DEFINE_AUTO_PTR(ysfx_state_u, ysfx_state_t, ysfx_state_free);
YSFX_DEFINE_AUTO_PTR(ysfx_bank_u, ysfx_bank_t, ysfx_bank_free);
#endif // defined(__cplusplus) && (__cplusplus >= 201103L || (defined(_MSC_VER) && _MSVC_LANG >= 201103L))

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

#endif // !defined(YSFX_INCLUDED_YSFX_H)

+ 139
- 0
source/modules/ysfx/sources/base64/Base64.hpp View File

@@ -0,0 +1,139 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2022 Jean Pierre Cimalando <jp-dev@inbox.ru>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#pragma once
#include <cstdint>
#include <cstring>
#include <cassert>
#include <array>
#include <vector>

// -----------------------------------------------------------------------
// base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html

/*
Copyright (C) 2004-2008 René Nyffenegger

This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.

2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original source code.

3. This notice may not be removed or altered from any source distribution.

René Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/

// -----------------------------------------------------------------------
// Helpers

#ifndef DOXYGEN
namespace DistrhoBase64Helpers {

static
std::array<int8_t, 256> createCharIndexTable()
{
std::array<int8_t, 256> table;
table.fill(-1);
int8_t index = 0;
for (uint8_t c = 'A'; c <= 'Z'; ++c) table[c] = index++;
for (uint8_t c = 'a'; c <= 'z'; ++c) table[c] = index++;
for (uint8_t c = '0'; c <= '9'; ++c) table[c] = index++;
table['+'] = index++;
table['/'] = index++;
return table;
}

static const std::array<int8_t, 256> kCharIndexTable = createCharIndexTable();

} // namespace DistrhoBase64Helpers
#endif

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

static inline
std::vector<uint8_t> d_getChunkFromBase64String(const char* const base64string, std::size_t base64stringLen = ~std::size_t(0))
{
std::vector<uint8_t> ret;

if (! base64string)
return ret;

uint32_t i=0, j=0;
uint32_t charArray3[3], charArray4[4];

if (base64stringLen == ~std::size_t(0))
base64stringLen = std::strlen(base64string);

ret.reserve(base64stringLen*3/4 + 4);

for (std::size_t l=0; l<base64stringLen; ++l)
{
const unsigned char c = base64string[l];

if (c == '\0' || c == '=')
break;
if (DistrhoBase64Helpers::kCharIndexTable[c] == -1)
continue;

charArray4[i++] = static_cast<uint32_t>(c);

if (i == 4)
{
for (i=0; i<4; ++i)
charArray4[i] = static_cast<uint8_t>(DistrhoBase64Helpers::kCharIndexTable[charArray4[i]]);

charArray3[0] = (charArray4[0] << 2) + ((charArray4[1] & 0x30) >> 4);
charArray3[1] = ((charArray4[1] & 0xf) << 4) + ((charArray4[2] & 0x3c) >> 2);
charArray3[2] = ((charArray4[2] & 0x3) << 6) + charArray4[3];

for (i=0; i<3; ++i)
ret.push_back(static_cast<uint8_t>(charArray3[i]));

i = 0;
}
}

if (i != 0)
{
for (j=0; j<i && j<4; ++j)
charArray4[j] = static_cast<uint8_t>(DistrhoBase64Helpers::kCharIndexTable[charArray4[j]]);

for (j=i; j<4; ++j)
charArray4[j] = 0;

charArray3[0] = (charArray4[0] << 2) + ((charArray4[1] & 0x30) >> 4);
charArray3[1] = ((charArray4[1] & 0xf) << 4) + ((charArray4[2] & 0x3c) >> 2);
charArray3[2] = ((charArray4[2] & 0x3) << 6) + charArray4[3];

for (j=0; i>0 && j<i-1; j++)
ret.push_back(static_cast<uint8_t>(charArray3[j]));
}

return ret;
}

+ 20
- 0
source/modules/ysfx/sources/eel2-gas/LICENSE.txt View File

@@ -0,0 +1,20 @@
Copyright (C) 2005 and later Cockos Incorporated
Copyright (C) 2021 and later Jean Pierre Cimalando

Portions copyright other contributors, see each source file for more information

This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

+ 1399
- 0
source/modules/ysfx/sources/eel2-gas/sources/asm-nseel-x64-sse.S
File diff suppressed because it is too large
View File


+ 1
- 0
source/modules/ysfx/sources/eel2-gas/sources/ref-hash-sha512.txt View File

@@ -0,0 +1 @@
157d18abd0ac1fe6c4cf59c6f8fbe2c146b39079cd2cc9987837835d4c998e03c2b4816d9e1de13144692081f171d66659b7d1ec735d995b64ee1b033d5f3801

+ 62
- 0
source/modules/ysfx/sources/lice_stb/lice_stb_bmp.cpp View File

@@ -0,0 +1,62 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#define STBI_ONLY_BMP
#include "lice_stb_generic.hpp"

LICE_IBitmap *LICE_LoadBMP(const char *filename, LICE_IBitmap *bmp)
{
return LICE_LoadSTB(filename, bmp);
}

class LICE_stb_BMPLoader
{
_LICE_ImageLoader_rec rec;

public:
LICE_stb_BMPLoader()
{
rec.loadfunc = loadfunc;
rec.get_extlist = get_extlist;
rec._next = LICE_ImageLoader_list;
LICE_ImageLoader_list = &rec;
}

static LICE_IBitmap *loadfunc(const char *filename, bool checkFileName, LICE_IBitmap *bmpbase)
{
if (checkFileName) {
const char *p = filename;
while (*p)
p++;
while (p > filename && *p != '\\' && *p != '/' && *p != '.')
p--;
if (stricmp(p, ".bmp"))
return nullptr;
}
return LICE_LoadSTB(filename, bmpbase);
}

static const char *get_extlist()
{
return "BMP files (*.BMP)\0*.BMP\0";
}
};

void lice_stb_install_bmp_loader()
{
static LICE_stb_BMPLoader loader;
}

+ 76
- 0
source/modules/ysfx/sources/lice_stb/lice_stb_generic.hpp View File

@@ -0,0 +1,76 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_STATIC
#define STBI_WINDOWS_UTF8

#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunused-function"
#endif
#include "stb_image.h"
#if defined(__GNUC__)
# pragma GCC diagnostic pop
#endif

//------------------------------------------------------------------------------
#include "WDL/lice/lice.h"
#include "WDL/wdltypes.h"

static LICE_IBitmap *LICE_LoadSTB(const char *filename, LICE_IBitmap *bmp)
{
LICE_IBitmap *delbmp = nullptr;
stbi_uc *srcpx = nullptr;
LICE_pixel *dstpx = nullptr;
bool dstflip = false;
unsigned dstspan = 0;
unsigned w = 0;
unsigned h = 0;
unsigned ch = 0;

srcpx = stbi_load(filename, (int *)&w, (int *)&h, (int *)&ch, 4);
if (!srcpx)
goto fail;

if (bmp)
bmp->resize(w, h);
else
bmp = delbmp = new WDL_NEW LICE_MemBitmap(w, h);

if (!bmp || (unsigned)bmp->getWidth() != w || (unsigned)bmp->getHeight() != h)
goto fail;

dstpx = bmp->getBits();
dstflip = bmp->isFlipped();
dstspan = bmp->getRowSpan();

for (unsigned row = 0; row < h; ++row) {
const stbi_uc *src = srcpx + row * (4 * w);
LICE_pixel *dst = dstpx + dstspan * (dstflip ? (h - 1 - row) : row);
for (unsigned col = 0; col < w; ++col, src += 4, ++dst)
*dst = LICE_RGBA(src[0], src[1], src[2], src[3]);
}

stbi_image_free(srcpx);
return bmp;

fail:
delete delbmp;
stbi_image_free(srcpx);
return nullptr;
}

+ 67
- 0
source/modules/ysfx/sources/lice_stb/lice_stb_gif.cpp View File

@@ -0,0 +1,67 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#define STBI_ONLY_GIF
#include "lice_stb_generic.hpp"

LICE_IBitmap *LICE_LoadGIF(const char *filename, LICE_IBitmap *bmp, int *nframes)
{
LICE_IBitmap *ret = LICE_LoadSTB(filename, bmp);
if (ret) {
if (nframes) // animation not implemented
*nframes = 1;
}
return ret;
}

class LICE_stb_GIFLoader
{
_LICE_ImageLoader_rec rec;

public:
LICE_stb_GIFLoader()
{
rec.loadfunc = loadfunc;
rec.get_extlist = get_extlist;
rec._next = LICE_ImageLoader_list;
LICE_ImageLoader_list = &rec;
}

static LICE_IBitmap *loadfunc(const char *filename, bool checkFileName, LICE_IBitmap *bmpbase)
{
if (checkFileName) {
const char *p = filename;
while (*p)
p++;
while (p > filename && *p != '\\' && *p != '/' && *p != '.')
p--;
if (stricmp(p, ".gif"))
return nullptr;
}
return LICE_LoadSTB(filename, bmpbase);
}

static const char *get_extlist()
{
return "GIF files (*.GIF)\0*.GIF\0";
}
};

void lice_stb_install_gif_loader()
{
static LICE_stb_GIFLoader loader;
}

+ 62
- 0
source/modules/ysfx/sources/lice_stb/lice_stb_jpg.cpp View File

@@ -0,0 +1,62 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#define STBI_ONLY_JPEG
#include "lice_stb_generic.hpp"

LICE_IBitmap *LICE_LoadJPG(const char *filename, LICE_IBitmap *bmp)
{
return LICE_LoadSTB(filename, bmp);
}

class LICE_stb_JPGLoader
{
_LICE_ImageLoader_rec rec;

public:
LICE_stb_JPGLoader()
{
rec.loadfunc = loadfunc;
rec.get_extlist = get_extlist;
rec._next = LICE_ImageLoader_list;
LICE_ImageLoader_list = &rec;
}

static LICE_IBitmap *loadfunc(const char *filename, bool checkFileName, LICE_IBitmap *jpgbase)
{
if (checkFileName) {
const char *p = filename;
while (*p)
p++;
while (p > filename && *p != '\\' && *p != '/' && *p != '.')
p--;
if (stricmp(p, ".jpg") && stricmp(p, ".jpeg") && stricmp(p, ".jfif"))
return nullptr;
}
return LICE_LoadSTB(filename, jpgbase);
}

static const char *get_extlist()
{
return "JPEG files (*.JPG;*.JPEG;*.JFIF)\0*.JPG;*.JPEG;*.JFIF\0";
}
};

void lice_stb_install_jpg_loader()
{
static LICE_stb_JPGLoader loader;
}

+ 26
- 0
source/modules/ysfx/sources/lice_stb/lice_stb_loaders.cpp View File

@@ -0,0 +1,26 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#include "lice_stb_loaders.hpp"

void lice_stb_install_loaders()
{
lice_stb_install_bmp_loader();
lice_stb_install_gif_loader();
lice_stb_install_jpg_loader();
lice_stb_install_png_loader();
}

+ 23
- 0
source/modules/ysfx/sources/lice_stb/lice_stb_loaders.hpp View File

@@ -0,0 +1,23 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

void lice_stb_install_loaders();

void lice_stb_install_bmp_loader();
void lice_stb_install_gif_loader();
void lice_stb_install_jpg_loader();
void lice_stb_install_png_loader();

+ 62
- 0
source/modules/ysfx/sources/lice_stb/lice_stb_png.cpp View File

@@ -0,0 +1,62 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#define STBI_ONLY_PNG
#include "lice_stb_generic.hpp"

LICE_IBitmap *LICE_LoadPNG(const char *filename, LICE_IBitmap *bmp)
{
return LICE_LoadSTB(filename, bmp);
}

class LICE_stb_PNGLoader
{
_LICE_ImageLoader_rec rec;

public:
LICE_stb_PNGLoader()
{
rec.loadfunc = loadfunc;
rec.get_extlist = get_extlist;
rec._next = LICE_ImageLoader_list;
LICE_ImageLoader_list = &rec;
}

static LICE_IBitmap *loadfunc(const char *filename, bool checkFileName, LICE_IBitmap *bmpbase)
{
if (checkFileName) {
const char *p = filename;
while (*p)
p++;
while (p > filename && *p != '\\' && *p != '/' && *p != '.')
p--;
if (stricmp(p, ".png"))
return nullptr;
}
return LICE_LoadSTB(filename, bmpbase);
}

static const char *get_extlist()
{
return "PNG files (*.PNG)\0*.PNG\0";
}
};

void lice_stb_install_png_loader()
{
static LICE_stb_PNGLoader loader;
}

+ 101
- 0
source/modules/ysfx/sources/lice_stb/lice_stb_write.cpp View File

@@ -0,0 +1,101 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#define STBIW_WINDOWS_UTF8
#define STB_IMAGE_WRITE_STATIC
#define STB_IMAGE_WRITE_IMPLEMENTATION

#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunused-function"
#endif
#include "stb_image_write.h"
#if defined(__GNUC__)
# pragma GCC diagnostic pop
#endif

//------------------------------------------------------------------------------
#define WDL_NO_DEFINE_MINMAX
#include "WDL/lice/lice.h"
#include "WDL/wdltypes.h"
#include <memory>

static std::unique_ptr<unsigned char[]> bmp_to_stbi(LICE_IBitmap *bmp, unsigned ch)
{
unsigned w = (unsigned)bmp->getWidth();
unsigned h = (unsigned)bmp->getHeight();
unsigned srcspan = (unsigned)bmp->getRowSpan();
bool srcflip = bmp->isFlipped();
LICE_pixel *srcpx = bmp->getBits();
std::unique_ptr<unsigned char[]> result;

if (ch == 4) {
unsigned char *dstpx = new unsigned char[4 * w * h];
result.reset(dstpx);
for (unsigned row = 0; row < h; ++row) {
LICE_pixel *src = srcpx + srcspan * (srcflip ? (h - 1 - row) : row);
unsigned char *dst = dstpx + row * (4 * w);
for (unsigned col = 0; col < w; ++col, ++src, dst += 4) {
LICE_pixel px = *src;
dst[0] = LICE_GETR(px);
dst[1] = LICE_GETG(px);
dst[2] = LICE_GETB(px);
dst[3] = LICE_GETA(px);
}
}
}
else if (ch == 3) {
unsigned char *dstpx = new unsigned char[3 * w * h];
result.reset(dstpx);
for (unsigned row = 0; row < h; ++row) {
LICE_pixel *src = srcpx + srcspan * (srcflip ? (h - 1 - row) : row);
unsigned char *dst = dstpx + row * (3 * w);
for (unsigned col = 0; col < w; ++col, ++src, dst += 3) {
LICE_pixel px = *src;
dst[0] = LICE_GETR(px);
dst[1] = LICE_GETG(px);
dst[2] = LICE_GETB(px);
}
}
}

return result;
}

bool LICE_WritePNG(const char *filename, LICE_IBitmap *bmp, bool wantalpha)
{
unsigned ch = wantalpha ? 4 : 3;
std::unique_ptr<unsigned char[]> data = bmp_to_stbi(bmp, ch);

if (!data)
return false;

return stbi_write_png(filename, bmp->getWidth(), bmp->getHeight(), (int)ch, data.get(), 0);
}

bool LICE_WriteJPG(const char *filename, LICE_IBitmap *bmp, int quality, bool force_baseline)
{
(void)force_baseline; // always baseline

unsigned ch = 4;
std::unique_ptr<unsigned char[]> data = bmp_to_stbi(bmp, ch);

if (!data)
return false;

return stbi_write_jpg(filename, bmp->getWidth(), bmp->getHeight(), (int)ch, data.get(), quality);
}

+ 169
- 0
source/modules/ysfx/sources/utility/sync_bitset.hpp View File

@@ -0,0 +1,169 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#pragma once
#include <type_traits>
#include <atomic>
#include <cstdint>

namespace ysfx {

//------------------------------------------------------------------------------
// sync_bitset64: A lock-free synchronized bitset of size 64
//
// This is implemented by a single qword on 64-bit machine, otherwise a pair of
// dwords on 32-bit machine, which is to ensure lock-freedom.
//
// This bitset is synchronized but not atomic; a thread might see a
// partially updated set after a modification. Conceptually, it can be seen as
// atomic on individual bit flips (that is, masking operations do not race).

class sync_bitset64_single;
class sync_bitset64_dual;

// could use std::atomic::is_always_lock_free on C++17 and up
using sync_bitset64 = std::conditional<
sizeof(intptr_t) <= 4, sync_bitset64_dual, sync_bitset64_single>::type;

//------------------------------------------------------------------------------
class sync_bitset64_single {
public:
uint64_t load() const
{
return bits_.load(std::memory_order_relaxed);
}

void store(uint64_t value)
{
bits_.store(value, std::memory_order_relaxed);
}

uint64_t exchange(uint64_t value)
{
return bits_.exchange(value, std::memory_order_relaxed);
}

uint64_t fetch_or(uint64_t value)
{
return bits_.fetch_or(value, std::memory_order_relaxed);
}

uint64_t fetch_and(uint64_t value)
{
return bits_.fetch_and(value, std::memory_order_relaxed);
}

uint64_t fetch_xor(uint64_t value)
{
return bits_.fetch_xor(value, std::memory_order_relaxed);
}

void operator|=(uint64_t value)
{
fetch_or(value);
}

void operator&=(uint64_t value)
{
fetch_and(value);
}

void operator^=(uint64_t value)
{
fetch_xor(value);
}

private:
std::atomic<uint64_t> bits_{0};
};

//------------------------------------------------------------------------------
class sync_bitset64_dual {
public:
uint64_t load() const
{
return join(lobits_.load(std::memory_order_relaxed),
hibits_.load(std::memory_order_relaxed));
}

void store(uint64_t value)
{
lobits_.store(lo(value), std::memory_order_relaxed);
hibits_.store(hi(value), std::memory_order_relaxed);
}

uint64_t exchange(uint64_t value)
{
return join(lobits_.exchange(lo(value), std::memory_order_relaxed),
hibits_.exchange(hi(value), std::memory_order_relaxed));
}

uint64_t fetch_or(uint64_t value)
{
return join(lobits_.fetch_or(lo(value), std::memory_order_relaxed),
hibits_.fetch_or(hi(value), std::memory_order_relaxed));
}

uint64_t fetch_and(uint64_t value)
{
return join(lobits_.fetch_and(lo(value), std::memory_order_relaxed),
hibits_.fetch_and(hi(value), std::memory_order_relaxed));
}

uint64_t fetch_xor(uint64_t value)
{
return join(lobits_.fetch_xor(lo(value), std::memory_order_relaxed),
hibits_.fetch_xor(hi(value), std::memory_order_relaxed));
}

void operator|=(uint64_t value)
{
fetch_or(value);
}

void operator&=(uint64_t value)
{
fetch_and(value);
}

void operator^=(uint64_t value)
{
fetch_xor(value);
}

private:
static constexpr uint64_t join(uint32_t lo, uint32_t hi)
{
return (uint64_t)lo | ((uint64_t)hi << 32);
}

static constexpr uint32_t lo(uint64_t value)
{
return (uint32_t)(value & 0xFFFFFFFFu);
}

static constexpr uint32_t hi(uint64_t value)
{
return (uint32_t)(value >> 32);
}

private:
std::atomic<uint32_t> lobits_{0};
std::atomic<uint32_t> hibits_{0};
};

} // namespace ysfx

+ 1540
- 0
source/modules/ysfx/sources/ysfx.cpp
File diff suppressed because it is too large
View File


+ 192
- 0
source/modules/ysfx/sources/ysfx.hpp View File

@@ -0,0 +1,192 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#pragma once
#include "ysfx.h"
#include "ysfx_midi.hpp"
#include "ysfx_parse.hpp"
#include "ysfx_api_eel.hpp"
#include "ysfx_api_reaper.hpp"
#include "ysfx_api_file.hpp"
#include "ysfx_api_gfx.hpp"
#include "ysfx_utils.hpp"
#include "utility/sync_bitset.hpp"
#include "WDL/eel2/ns-eel.h"
#include "WDL/eel2/ns-eel-int.h"
#include <unordered_map>
#include <atomic>

YSFX_DEFINE_AUTO_PTR(NSEEL_VMCTX_u, void, NSEEL_VM_free); // NOTE: `NSEEL_VMCTX` is `void *`
YSFX_DEFINE_AUTO_PTR(NSEEL_CODEHANDLE_u, void, NSEEL_code_free); // NOTE: `NSEEL_CODEHANDLE` is `void *`

struct ysfx_source_unit_t {
ysfx_toplevel_t toplevel;
ysfx_header_t header;
};
using ysfx_source_unit_u = std::unique_ptr< ysfx_source_unit_t>;

enum ysfx_file_type_t {
ysfx_file_type_none,
ysfx_file_type_txt,
ysfx_file_type_raw,
ysfx_file_type_audio,
};

enum ysfx_thread_id_t {
ysfx_thread_id_none,
ysfx_thread_id_dsp,
ysfx_thread_id_gfx,
};

struct ysfx_s {
ysfx_config_u config;
eel_string_context_state_u string_ctx;
ysfx::mutex string_mutex;
ysfx::mutex atomic_mutex;
NSEEL_VMCTX_u vm;

// some default values, these are not standard, just arbitrary
uint32_t block_size = 128;
ysfx_real sample_rate = 44100;
uint32_t valid_input_channels = 2;

bool is_freshly_compiled = false;
bool must_compute_init = false;
bool must_compute_slider = false;

std::unordered_map<ysfx_real *, uint32_t> slider_of_var;

// source
struct {
std::string main_file_path;
std::string bank_path;
ysfx_source_unit_u main;
std::vector<ysfx_source_unit_u> imports;
std::unordered_map<std::string, uint32_t> slider_alias;
} source;

// compilation
struct {
bool compiled = false;
std::vector<NSEEL_CODEHANDLE_u> init;
NSEEL_CODEHANDLE_u slider;
NSEEL_CODEHANDLE_u block;
NSEEL_CODEHANDLE_u sample;
NSEEL_CODEHANDLE_u gfx;
NSEEL_CODEHANDLE_u serialize;
} code;

// VM variables
struct {
EEL_F *spl[ysfx_max_channels] = {};
EEL_F *slider[ysfx_max_sliders] = {};
EEL_F *srate = nullptr;
EEL_F *num_ch = nullptr;
EEL_F *samplesblock = nullptr;
EEL_F *trigger = nullptr;
EEL_F *tempo = nullptr;
EEL_F *play_state = nullptr;
EEL_F *play_position = nullptr;
EEL_F *beat_position = nullptr;
EEL_F *ts_num = nullptr;
EEL_F *ts_denom = nullptr;
EEL_F *ext_noinit = nullptr;
EEL_F *ext_nodenorm = nullptr;
EEL_F *ext_midi_bus = nullptr;
EEL_F *midi_bus = nullptr;
EEL_F *pdc_delay = nullptr;
EEL_F *pdc_bot_ch = nullptr;
EEL_F *pdc_top_ch = nullptr;
EEL_F *pdc_midi = nullptr;
// gfx variables
EEL_F *gfx_r = nullptr;
EEL_F *gfx_g = nullptr;
EEL_F *gfx_b = nullptr;
EEL_F *gfx_a = nullptr;
EEL_F *gfx_a2 = nullptr;
EEL_F *gfx_w = nullptr;
EEL_F *gfx_h = nullptr;
EEL_F *gfx_x = nullptr;
EEL_F *gfx_y = nullptr;
EEL_F *gfx_mode = nullptr;
EEL_F *gfx_clear = nullptr;
EEL_F *gfx_texth = nullptr;
EEL_F *gfx_dest = nullptr;
EEL_F *gfx_ext_retina = nullptr;
EEL_F *mouse_x = nullptr;
EEL_F *mouse_y = nullptr;
EEL_F *mouse_cap = nullptr;
EEL_F *mouse_wheel = nullptr;
EEL_F *mouse_hwheel = nullptr;
// other
EEL_F ret_temp = 0;
} var;

// MIDI
struct {
ysfx_midi_buffer_u in;
ysfx_midi_buffer_u out;
} midi;

// Slider
struct {
ysfx::sync_bitset64 automate_mask;
ysfx::sync_bitset64 change_mask;
ysfx::sync_bitset64 visible_mask;
} slider;

// Triggers
uint32_t triggers = 0;

// Files
struct {
std::vector<ysfx_file_u> list;
ysfx::mutex list_mutex;
} file;

#if !defined(YSFX_NO_GFX)
// Graphics
struct {
ysfx_gfx_state_u state;
ysfx::mutex mutex;
volatile bool ready = false;
volatile bool wants_retina = false;
std::atomic<bool> must_init{false};
} gfx;
#endif

std::atomic<uint32_t> ref_count{1};
};

ysfx_thread_id_t ysfx_get_thread_id();
void ysfx_set_thread_id(ysfx_thread_id_t id);
void ysfx_unload_source(ysfx_t *fx);
void ysfx_unload_code(ysfx_t *fx);
void ysfx_first_init(ysfx_t *fx);
void ysfx_update_slider_visibility_mask(ysfx_t *fx);
void ysfx_fill_file_enums(ysfx_t *fx);
void ysfx_fix_invalid_enums(ysfx_t *fx);
ysfx_section_t *ysfx_search_section(ysfx_t *fx, uint32_t type, ysfx_toplevel_t **origin = nullptr);
std::string ysfx_resolve_import_path(ysfx_t *fx, const std::string &name, const std::string &origin);
uint32_t ysfx_current_midi_bus(ysfx_t *fx);
void ysfx_clear_files(ysfx_t *fx);
ysfx_file_t *ysfx_get_file(ysfx_t *fx, uint32_t handle, std::unique_lock<ysfx::mutex> &lock, std::unique_lock<ysfx::mutex> *list_lock = nullptr);
int32_t ysfx_insert_file(ysfx_t *fx, ysfx_file_t *file);
void ysfx_serialize(ysfx_t *fx);
uint32_t ysfx_get_slider_of_var(ysfx_t *fx, EEL_F *var);
bool ysfx_find_data_file(ysfx_t *fx, EEL_F *file, std::string &result);
ysfx_file_type_t ysfx_detect_file_type(ysfx_t *fx, const char *path, void **fmtobj);

+ 144
- 0
source/modules/ysfx/sources/ysfx_api_eel.cpp View File

@@ -0,0 +1,144 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#include "ysfx.hpp"
#include "ysfx_api_eel.hpp"
#include "ysfx_utils.hpp"
#include <cstring>
#include <cstdlib>
#include <cstddef>

#include "WDL/ptrlist.h"
#include "WDL/assocarray.h"
#include "WDL/mutex.h"

#ifndef EELSCRIPT_NO_STDIO
# define EEL_STRING_STDOUT_WRITE(x,len) { fwrite(x,len,1,stdout); fflush(stdout); }
#endif

//TODO: thread-safety considerations with strings

#define EEL_STRING_MAXUSERSTRING_LENGTH_HINT ysfx_string_max_length

static ysfx::mutex atomic_mutex;
#define EEL_ATOMIC_SET_SCOPE(opaque) ysfx::mutex *mutex = ((opaque) ? &((ysfx_t *)(opaque))->atomic_mutex : &atomic_mutex);
#define EEL_ATOMIC_ENTER mutex->lock()
#define EEL_ATOMIC_LEAVE mutex->unlock()

#include "WDL/eel2/eel_strings.h"
#include "WDL/eel2/eel_misc.h"
#include "WDL/eel2/eel_fft.h"
#include "WDL/eel2/eel_mdct.h"
#include "WDL/eel2/eel_atomic.h"

//------------------------------------------------------------------------------
void ysfx_api_init_eel()
{
EEL_string_register();
EEL_fft_register();
EEL_mdct_register();
EEL_string_register();
EEL_misc_register();
EEL_atomic_register();
}

//------------------------------------------------------------------------------
void ysfx_eel_string_initvm(NSEEL_VMCTX vm)
{
eel_string_initvm(vm);
}

//------------------------------------------------------------------------------
eel_string_context_state *ysfx_eel_string_context_new()
{
return new eel_string_context_state;
}

void ysfx_eel_string_context_free(eel_string_context_state *state)
{
delete state;
}

void ysfx_eel_string_context_update_named_vars(eel_string_context_state *state, NSEEL_VMCTX vm)
{
state->update_named_vars(vm);
}

//------------------------------------------------------------------------------
static_assert(
ysfx_string_max_length == EEL_STRING_MAXUSERSTRING_LENGTH_HINT,
"string max lengths do not match");

bool ysfx_string_access(ysfx_t *fx, ysfx_real id, bool for_write, void (*access)(void *, WDL_FastString &), void *userdata)
{
void *opaque = fx;
eel_string_context_state *ctx = EEL_STRING_GET_CONTEXT_POINTER(opaque);
EEL_STRING_MUTEXLOCK_SCOPE

EEL_STRING_STORAGECLASS *wr = nullptr;
ctx->GetStringForIndex(id, &wr, for_write);
if (!wr)
return false;

access(userdata, *wr);
return true;
}

bool ysfx_string_get(ysfx_t *fx, ysfx_real id, std::string &txt)
{
return ysfx_string_access(fx, id, false, [](void *ud, WDL_FastString &str) {
((std::string *)ud)->assign(str.Get(), (uint32_t)str.GetLength());
}, &txt);
}

bool ysfx_string_set(ysfx_t *fx, ysfx_real id, const std::string &txt)
{
return ysfx_string_access(fx, id, true, [](void *ud, WDL_FastString &str) {
const std::string *txt = (const std::string *)ud;
size_t size = txt->size();
if (size > ysfx_string_max_length)
size = ysfx_string_max_length;
str.SetRaw(txt->data(), (int)size);
}, (void *)&txt);
}

void ysfx_string_lock(ysfx_t *fx)
{
fx->string_mutex.lock();
}

void ysfx_string_unlock(ysfx_t *fx)
{
fx->string_mutex.unlock();
}

const char *ysfx_string_access_unlocked(ysfx_t *fx, ysfx_real id, WDL_FastString **fs, bool for_write)
{
return fx->string_ctx->GetStringForIndex(id, fs, for_write);
}

//------------------------------------------------------------------------------
// NOTE(jpc) implement this? I guess probably not.
// DSP and UI should not mutex each other.

void NSEEL_HOSTSTUB_EnterMutex()
{
}

void NSEEL_HOSTSTUB_LeaveMutex()
{
}

+ 57
- 0
source/modules/ysfx/sources/ysfx_api_eel.hpp View File

@@ -0,0 +1,57 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#pragma once
#include "ysfx.h"
#include <string>

typedef void *NSEEL_VMCTX;
class WDL_FastString;

//------------------------------------------------------------------------------
void ysfx_api_init_eel();

//------------------------------------------------------------------------------
void ysfx_eel_string_initvm(NSEEL_VMCTX vm);

//------------------------------------------------------------------------------
class eel_string_context_state;
eel_string_context_state *ysfx_eel_string_context_new();
void ysfx_eel_string_context_free(eel_string_context_state *state);
void ysfx_eel_string_context_update_named_vars(eel_string_context_state *state, NSEEL_VMCTX vm);
YSFX_DEFINE_AUTO_PTR(eel_string_context_state_u, eel_string_context_state, ysfx_eel_string_context_free);

//------------------------------------------------------------------------------
enum { ysfx_string_max_length = 1 << 16 };
bool ysfx_string_access(ysfx_t *fx, ysfx_real id, bool for_write, void (*access)(void *, WDL_FastString &), void *userdata);
bool ysfx_string_get(ysfx_t *fx, ysfx_real id, std::string &txt);
bool ysfx_string_set(ysfx_t *fx, ysfx_real id, const std::string &txt);
void ysfx_string_lock(ysfx_t *fx);
void ysfx_string_unlock(ysfx_t *fx);
const char *ysfx_string_access_unlocked(ysfx_t *fx, ysfx_real id, WDL_FastString **fs, bool for_write);

struct ysfx_string_scoped_lock {
ysfx_string_scoped_lock(ysfx_t *fx) : m_fx(fx) { ysfx_string_lock(fx); }
~ysfx_string_scoped_lock() { ysfx_string_unlock(m_fx); }
private:
ysfx_t *m_fx = nullptr;
};

#define EEL_STRING_GET_CONTEXT_POINTER(opaque) (((ysfx_t *)(opaque))->string_ctx.get())
#define EEL_STRING_GET_FOR_INDEX(x, wr) (ysfx_string_access_unlocked((ysfx_t *)(opaque), x, wr, false))
#define EEL_STRING_GET_FOR_WRITE(x, wr) (ysfx_string_access_unlocked((ysfx_t *)(opaque), x, wr, true))
#define EEL_STRING_MUTEXLOCK_SCOPE ysfx_string_scoped_lock lock{(ysfx_t *)(opaque)};

+ 574
- 0
source/modules/ysfx/sources/ysfx_api_file.cpp View File

@@ -0,0 +1,574 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#include "ysfx.hpp"
#include "ysfx_config.hpp"
#include "ysfx_api_file.hpp"
#include "ysfx_eel_utils.hpp"
#include <cstring>
#include <cstdio>
#include <cassert>

ysfx_raw_file_t::ysfx_raw_file_t(NSEEL_VMCTX vm, const char *filename)
: m_vm(vm),
m_stream(ysfx::fopen_utf8(filename, "rb"))
{
}

int32_t ysfx_raw_file_t::avail()
{
if (!m_stream)
return 0;

int64_t cur_off = ysfx::ftell_lfs(m_stream.get());
if (cur_off == -1)
return 0;

if (ysfx::fseek_lfs(m_stream.get(), 0, SEEK_END) == -1)
return 0;

int64_t end_off = ysfx::ftell_lfs(m_stream.get());
if (end_off == -1)
return 0;

if (ysfx::fseek_lfs(m_stream.get(), cur_off, SEEK_SET) == -1)
return 0;

if ((uint64_t)end_off < (uint64_t)cur_off)
return 0;

uint64_t byte_count = (uint64_t)end_off - (uint64_t)cur_off;
uint64_t f32_count = byte_count / 4;
return (f32_count > 0x7fffffff) ? 0x7fffffff : (uint32_t)f32_count;
}

void ysfx_raw_file_t::rewind()
{
if (!m_stream)
return;

::rewind(m_stream.get());
}

bool ysfx_raw_file_t::var(ysfx_real *var)
{
if (!m_stream)
return false;

uint8_t data[4];
if (fread(data, 1, 4, m_stream.get()) != 4)
return false;

*var = (EEL_F)ysfx::unpack_f32le(data);
return true;
}

uint32_t ysfx_raw_file_t::mem(uint32_t offset, uint32_t length)
{
if (!m_stream)
return 0;

ysfx_eel_ram_writer writer{m_vm, offset};

uint32_t read;
for (read = 0; read < length; ++read) {
ysfx_real value;
if (!var(&value))
break;
writer.write_next(value);
}

return read;
}

uint32_t ysfx_raw_file_t::string(std::string &str)
{
if (!m_stream)
return 0;

uint8_t data[4];
if (fread(data, 1, 4, m_stream.get()) != 4)
return 0;

str.clear();

uint32_t srclen = ysfx::unpack_u32le(data);
str.reserve((srclen < ysfx_string_max_length) ? srclen : ysfx_string_max_length);

uint32_t count = 0;
for (int byte; count < srclen && (byte = fgetc(m_stream.get())) != EOF; ) {
if (str.size() < ysfx_string_max_length)
str.push_back((unsigned char)byte);
++count;
}
return count;
}

//------------------------------------------------------------------------------
ysfx_text_file_t::ysfx_text_file_t(NSEEL_VMCTX vm, const char *filename)
: m_vm(vm),
m_stream(ysfx::fopen_utf8(filename, "rb"))
{
m_buf.reserve(256);
}

int32_t ysfx_text_file_t::avail()
{
if (!m_stream || ferror(m_stream.get()))
return -1;

return (feof(m_stream.get()) == 0) ? 0 : 1;
}

void ysfx_text_file_t::rewind()
{
if (!m_stream)
return;

::rewind(m_stream.get());
}

bool ysfx_text_file_t::var(ysfx_real *var)
{
if (!m_stream)
return false;

//TODO support the expression language for arithmetic

int ch;
do {
// get the next number separated by newline or comma
// but skip invalid lines
m_buf.clear();
while ((ch = fgetc(m_stream.get())) != EOF && ch != '\n' && ch != ',')
m_buf.push_back((unsigned char)ch);
const char *startp = m_buf.c_str();
const char *endp = (char *)startp;
double value = ysfx::dot_strtod(startp, (char **)&endp);
if (endp != startp) {
*var = (EEL_F)value;
return true;
}
} while (ch != EOF);

return false;
}

uint32_t ysfx_text_file_t::mem(uint32_t offset, uint32_t length)
{
if (!m_stream)
return 0;

ysfx_eel_ram_writer writer{m_vm, offset};

uint32_t read;
for (read = 0; read < length; ++read) {
ysfx_real value;
if (!var(&value))
break;
writer.write_next(value);
}

return read;
}

uint32_t ysfx_text_file_t::string(std::string &str)
{
if (!m_stream)
return 0;

str.clear();
str.reserve(256);

int ch;
do {
ch = fgetc(m_stream.get());
if (ch != EOF && str.size() < ysfx_string_max_length)
str.push_back((unsigned char)ch);
} while (ch != EOF && ch != '\n');

return (uint32_t)str.size();
}

//------------------------------------------------------------------------------
ysfx_audio_file_t::ysfx_audio_file_t(NSEEL_VMCTX vm, const ysfx_audio_format_t &fmt, const char *filename)
: m_vm(vm),
m_fmt(fmt),
m_reader(fmt.open(filename), fmt.close)
{
}

int32_t ysfx_audio_file_t::avail()
{
if (!m_reader)
return -1;

uint64_t avail = m_fmt.avail(m_reader.get());
return (avail > 0x7fffffff) ? 0x7fffffff : (int32_t)avail;
}

void ysfx_audio_file_t::rewind()
{
if (!m_reader)
return;

m_fmt.rewind(m_reader.get());
}

bool ysfx_audio_file_t::var(ysfx_real *var)
{
if (!m_reader)
return false;

return m_fmt.read(m_reader.get(), var, 1) == 1;
}

uint32_t ysfx_audio_file_t::mem(uint32_t offset, uint32_t length)
{
if (!m_reader)
return 0;

uint32_t numread = 0;
ysfx_real *buf = m_buf.get();
ysfx_eel_ram_writer writer(m_vm, offset);

while (numread < length) {
uint32_t n = length - numread;
if (n > buffer_size)
n = buffer_size;

uint32_t m = (uint32_t)m_fmt.read(m_reader.get(), buf, n);
for (uint32_t i = 0; i < m; ++i)
writer.write_next(buf[i]);

numread += m;
if (m < n)
break;
}

return numread;
}

uint32_t ysfx_audio_file_t::string(std::string &str)
{
(void)str;
return 0;
}

bool ysfx_audio_file_t::riff(uint32_t &nch, ysfx_real &samplerate)
{
if (!m_reader)
return false;

ysfx_audio_file_info_t info = m_fmt.info(m_reader.get());
nch = info.channels;
samplerate = info.sample_rate;
return true;
}

//------------------------------------------------------------------------------
ysfx_serializer_t::ysfx_serializer_t(NSEEL_VMCTX vm)
: m_vm(vm)
{
}

void ysfx_serializer_t::begin(bool write, std::string &buffer)
{
m_write = (int)write;
m_buffer = &buffer;
m_pos = 0;
}

void ysfx_serializer_t::end()
{
m_write = -1;
m_buffer = nullptr;
}

int32_t ysfx_serializer_t::avail()
{
if (m_write)
return -1;
else
return 0;
}

void ysfx_serializer_t::rewind()
{
}

bool ysfx_serializer_t::var(ysfx_real *var)
{
if (m_write == 1) {
uint8_t buf[4];
ysfx::pack_f32le((float)*var, buf);
m_buffer->append((char *)buf, 4);
return true;
}
else if (m_write == 0) {
if (m_pos + 4 > m_buffer->size()) {
m_pos = m_buffer->size();
*var = 0;
return false;
}
*var = (EEL_F)ysfx::unpack_f32le((uint8_t *)&(*m_buffer)[m_pos]);
m_pos += 4;
return true;
}
return false;
}

uint32_t ysfx_serializer_t::mem(uint32_t offset, uint32_t length)
{
if (m_write == 1) {
ysfx_eel_ram_reader reader{m_vm, offset};
for (uint32_t i = 0; i < length; ++i) {
ysfx_real value = reader.read_next();
if (!var(&value))
return i;
}
return length;
}
else if (m_write == 0) {
ysfx_eel_ram_writer writer{m_vm, offset};
for (uint32_t i = 0; i < length; ++i) {
ysfx_real value{};
if (!var(&value))
return i;
writer.write_next(value);
}
return length;
}
return 0;
}

uint32_t ysfx_serializer_t::string(std::string &str)
{
// TODO implement me; docs claim support in Reaper 4.59+ but it seems
// non-working (as of Reaper 6.40)
return 0;
}

//------------------------------------------------------------------------------
static EEL_F NSEEL_CGEN_CALL ysfx_api_file_open(void *opaque, EEL_F *file_)
{
ysfx_t *fx = (ysfx_t *)opaque;

std::string filepath;
if (!ysfx_find_data_file(fx, file_, filepath))
return -1;

void *fmtobj = nullptr;
ysfx_file_type_t ftype = ysfx_detect_file_type(fx, filepath.c_str(), &fmtobj);

ysfx_file_u file;
switch (ftype) {
case ysfx_file_type_txt:
file.reset(new ysfx_text_file_t(fx->vm.get(), filepath.c_str()));
break;
case ysfx_file_type_raw:
file.reset(new ysfx_raw_file_t(fx->vm.get(), filepath.c_str()));
break;
case ysfx_file_type_audio:
file.reset(new ysfx_audio_file_t(fx->vm.get(), *(ysfx_audio_format_t *)fmtobj, filepath.c_str()));
break;
case ysfx_file_type_none:
break;
default:
assert(false);
}

if (file) {
int32_t handle = ysfx_insert_file(fx, file.get());
if (handle == -1)
return -1;
(void)file.release();
return (EEL_F)(uint32_t)handle;
}

return -1;
}

static EEL_F NSEEL_CGEN_CALL ysfx_api_file_close(void *opaque, EEL_F *handle_)
{
int32_t handle = ysfx_eel_round<int32_t>(*handle_);
if (handle <= 0) //NOTE: cannot close the serializer handle (0)
return -1;

ysfx_t *fx = (ysfx_t *)opaque;
std::unique_ptr<ysfx::mutex> file_mutex;
std::unique_lock<ysfx::mutex> lock;
std::unique_lock<ysfx::mutex> list_lock;

// hold both locks to protect file and list access during removal
if (!ysfx_get_file(fx, (uint32_t)handle, lock, &list_lock))
return -1;

// preserve the locked mutex of the object being removed
file_mutex = std::move(fx->file.list[(uint32_t)handle]->m_mutex);

fx->file.list[(uint32_t)handle].reset();
return 0;
}

static EEL_F *NSEEL_CGEN_CALL ysfx_api_file_rewind(void *opaque, EEL_F *handle_)
{
int32_t handle = ysfx_eel_round<int32_t>(*handle_);
if (handle < 0)
return handle_;

ysfx_t *fx = (ysfx_t *)opaque;
std::unique_lock<ysfx::mutex> lock;
ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
if (!file)
return 0;

file->rewind();
return handle_;
}

static EEL_F NSEEL_CGEN_CALL ysfx_api_file_var(void *opaque, EEL_F *handle_, EEL_F *var)
{
int32_t handle = ysfx_eel_round<int32_t>(*handle_);
if (handle < 0)
return 0;

ysfx_t *fx = (ysfx_t *)opaque;
std::unique_lock<ysfx::mutex> lock;
ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
if (!file)
return 0;

if (!file->var(var))
return 0;

return 1;
}

static EEL_F NSEEL_CGEN_CALL ysfx_api_file_mem(void *opaque, EEL_F *handle_, EEL_F *offset_, EEL_F *length_)
{
int32_t handle = ysfx_eel_round<int32_t>(*handle_);
int32_t offset = ysfx_eel_round<int32_t>(*offset_);
int32_t length = ysfx_eel_round<int32_t>(*length_);
if (handle < 0 || offset < 0 || length <= 0)
return 0;

ysfx_t *fx = (ysfx_t *)opaque;
std::unique_lock<ysfx::mutex> lock;
ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
if (!file)
return 0;

return (EEL_F)file->mem((uint32_t)offset, (uint32_t)length);
}

static EEL_F NSEEL_CGEN_CALL ysfx_api_file_avail(void *opaque, EEL_F *handle_)
{
int32_t handle = ysfx_eel_round<int32_t>(*handle_);
if (handle < 0)
return 0;

ysfx_t *fx = (ysfx_t *)opaque;
std::unique_lock<ysfx::mutex> lock;
ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
if (!file)
return 0;

return file->avail();
}

static EEL_F *NSEEL_CGEN_CALL ysfx_api_file_riff(void *opaque, EEL_F *handle_, EEL_F *nch_, EEL_F *samplerate_)
{
int32_t handle = ysfx_eel_round<int32_t>(*handle_);
if (handle < 0)
return 0;

ysfx_t *fx = (ysfx_t *)opaque;
std::unique_lock<ysfx::mutex> lock;
ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
if (!file) {
*nch_ = 0;
*samplerate_ = 0;
return nch_;
}

uint32_t nch = 0;
ysfx_real samplerate = 0;
if (!file->riff(nch, samplerate)) {
*nch_ = 0;
*samplerate_ = 0;
return nch_;
}

*nch_ = (EEL_F)nch;
*samplerate_ = samplerate;
return nch_;
}


static EEL_F NSEEL_CGEN_CALL ysfx_api_file_text(void *opaque, EEL_F *handle_)
{
int32_t handle = ysfx_eel_round<int32_t>(*handle_);
if (handle < 0)
return 0;

ysfx_t *fx = (ysfx_t *)opaque;
std::unique_lock<ysfx::mutex> lock;
ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
if (!file)
return 0;

return (EEL_F)file->is_text();
}

static EEL_F NSEEL_CGEN_CALL ysfx_api_file_string(void *opaque, EEL_F *handle_, EEL_F *string_)
{
int32_t handle = ysfx_eel_round<int32_t>(*handle_);
if (handle < 0)
return 0;

ysfx_t *fx = (ysfx_t *)opaque;
std::unique_lock<ysfx::mutex> lock;
ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
if (!file)
return 0;

std::string txt;
uint32_t count;
if (!file->is_in_write_mode()) {
count = file->string(txt);
ysfx_string_set(fx, *string_, txt);
}
else {
ysfx_string_get(fx, *string_, txt);
count = file->string(txt);
}
return (EEL_F)count;
}

void ysfx_api_init_file()
{
NSEEL_addfunc_retval("file_open", 1, NSEEL_PProc_THIS, &ysfx_api_file_open);
NSEEL_addfunc_retval("file_close", 1, NSEEL_PProc_THIS, &ysfx_api_file_close);
NSEEL_addfunc_retptr("file_rewind", 1, NSEEL_PProc_THIS, &ysfx_api_file_rewind);
NSEEL_addfunc_retval("file_var", 2, NSEEL_PProc_THIS, &ysfx_api_file_var);
NSEEL_addfunc_retval("file_mem", 3, NSEEL_PProc_THIS, &ysfx_api_file_mem);
NSEEL_addfunc_retval("file_avail", 1, NSEEL_PProc_THIS, &ysfx_api_file_avail);
NSEEL_addfunc_retptr("file_riff", 3, NSEEL_PProc_THIS, &ysfx_api_file_riff);
NSEEL_addfunc_retval("file_text", 1, NSEEL_PProc_THIS, &ysfx_api_file_text);
NSEEL_addfunc_retval("file_string", 2, NSEEL_PProc_THIS, &ysfx_api_file_string);
}

+ 127
- 0
source/modules/ysfx/sources/ysfx_api_file.hpp View File

@@ -0,0 +1,127 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#pragma once
#include "ysfx.h"
#include "ysfx_utils.hpp"
#include "WDL/eel2/ns-eel.h"
#include "WDL/eel2/ns-eel-int.h"
#include <vector>
#include <memory>

struct ysfx_file_t {
virtual ~ysfx_file_t() {}

virtual int32_t avail() = 0;
virtual void rewind() = 0;
virtual bool var(ysfx_real *var) = 0;
virtual uint32_t mem(uint32_t offset, uint32_t length) = 0;
virtual uint32_t string(std::string &str) = 0;
virtual bool riff(uint32_t &nch, ysfx_real &samplerate) = 0;
virtual bool is_text() = 0;
virtual bool is_in_write_mode() = 0;

std::unique_ptr<ysfx::mutex> m_mutex{new ysfx::mutex};
};

using ysfx_file_u = std::unique_ptr<ysfx_file_t>;

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

struct ysfx_raw_file_t final : ysfx_file_t {
ysfx_raw_file_t(NSEEL_VMCTX vm, const char *filename);

int32_t avail() override;
void rewind() override;
bool var(ysfx_real *var) override;
uint32_t mem(uint32_t offset, uint32_t length) override;
uint32_t string(std::string &str) override;
bool riff(uint32_t &, ysfx_real &) override { return false; }
bool is_text() override { return false; }
bool is_in_write_mode() override { return false; }

NSEEL_VMCTX m_vm = nullptr;
ysfx::FILE_u m_stream;
};

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

struct ysfx_text_file_t final : ysfx_file_t {
ysfx_text_file_t(NSEEL_VMCTX vm, const char *filename);

int32_t avail() override;
void rewind() override;
bool var(ysfx_real *var) override;
uint32_t mem(uint32_t offset, uint32_t length) override;
uint32_t string(std::string &str) override;
bool riff(uint32_t &, ysfx_real &) override { return false; }
bool is_text() override { return true; }
bool is_in_write_mode() override { return false; }

NSEEL_VMCTX m_vm = nullptr;
ysfx::FILE_u m_stream;
std::string m_buf;
};

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

struct ysfx_audio_file_t final : ysfx_file_t {
ysfx_audio_file_t(NSEEL_VMCTX vm, const ysfx_audio_format_t &fmt, const char *filename);

int32_t avail() override;
void rewind() override;
bool var(ysfx_real *var) override;
uint32_t mem(uint32_t offset, uint32_t length) override;
uint32_t string(std::string &str) override;
bool riff(uint32_t &nch, ysfx_real &samplerate) override;
bool is_text() override { return false; }
bool is_in_write_mode() override { return false; }

NSEEL_VMCTX m_vm = nullptr;
ysfx_audio_format_t m_fmt{};
std::unique_ptr<ysfx_audio_reader_t, void (*)(ysfx_audio_reader_t *)> m_reader;
enum { buffer_size = 256 };
std::unique_ptr<ysfx_real[]> m_buf{new ysfx_real[buffer_size]};
};

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

struct ysfx_serializer_t final : ysfx_file_t {
explicit ysfx_serializer_t(NSEEL_VMCTX vm);

void begin(bool write, std::string &buffer);
void end();

int32_t avail() override;
void rewind() override;
bool var(ysfx_real *var) override;
uint32_t mem(uint32_t offset, uint32_t length) override;
uint32_t string(std::string &str) override;
bool riff(uint32_t &, ysfx_real &) override { return false; }
bool is_text() override { return false; }
bool is_in_write_mode() override { return m_write == 1; }

NSEEL_VMCTX m_vm{};
int m_write = -1;
std::string *m_buffer = nullptr;
size_t m_pos = 0;
};

using ysfx_serializer_u = std::unique_ptr<ysfx_serializer_t>;

//------------------------------------------------------------------------------
void ysfx_api_init_file();

+ 470
- 0
source/modules/ysfx/sources/ysfx_api_gfx.cpp View File

@@ -0,0 +1,470 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#include "ysfx.hpp"
#include "ysfx_config.hpp"
#include "ysfx_api_gfx.hpp"
#include "ysfx_eel_utils.hpp"
#if !defined(YSFX_NO_GFX)
# include "lice_stb/lice_stb_loaders.hpp"
# define WDL_NO_DEFINE_MINMAX
# include "WDL/swell/swell.h"
# include "WDL/lice/lice.h"
# include "WDL/lice/lice_text.h"
# include "WDL/wdlstring.h"
#endif
#include <vector>
#include <queue>
#include <unordered_set>
#include <memory>
#include <atomic>
#include <cassert>

#if !defined(YSFX_NO_GFX)
#define GFX_GET_CONTEXT(opaque) (((opaque)) ? ysfx_gfx_get_context((ysfx_t *)(opaque)) : nullptr)

enum {
ysfx_gfx_max_images = 1024,
ysfx_gfx_max_fonts = 128,
ysfx_gfx_max_input = 1024,
};

class eel_lice_state;

struct ysfx_gfx_state_t {
ysfx_gfx_state_t(ysfx_t *fx);
~ysfx_gfx_state_t();
std::unique_ptr<eel_lice_state> lice;
std::queue<uint32_t> input_queue;
std::unordered_set<uint32_t> keys_pressed;
ysfx_real scale = 0.0;
void *callback_data = nullptr;
int (*show_menu)(void *, const char *, int32_t, int32_t) = nullptr;
void (*set_cursor)(void *, int32_t) = nullptr;
const char *(*get_drop_file)(void *user_data, int32_t index) = nullptr;
};

//------------------------------------------------------------------------------
#if !defined(YSFX_NO_GFX)
static bool eel_lice_get_filename_for_string(void *opaque, EEL_F idx, WDL_FastString *fs, int iswrite)
{
if (iswrite)
return false; // this is neither supported nor used

ysfx_t *fx = (ysfx_t *)opaque;

std::string filepath;
if (!ysfx_find_data_file(fx, &idx, filepath))
return false;

if (fs) fs->Set(filepath.data(), (uint32_t)filepath.size());
return true;
}

#define EEL_LICE_GET_FILENAME_FOR_STRING(idx, fs, p) \
eel_lice_get_filename_for_string(opaque, (idx), (fs), (p))

#endif

//------------------------------------------------------------------------------
#if !defined(YSFX_NO_GFX)
# include "ysfx_api_gfx_lice.hpp"
#else
# include "ysfx_api_gfx_dummy.hpp"
#endif

//------------------------------------------------------------------------------
#if !defined(YSFX_NO_GFX)

static bool translate_special_key(uint32_t uni_key, uint32_t &jsfx_key)
{
auto key_c = [](uint8_t a, uint8_t b, uint8_t c, uint8_t d) -> uint32_t {
return a | (b << 8) | (c << 16) | (d << 24);
};

switch (uni_key) {
default: return false;
case ysfx_key_delete: jsfx_key = key_c('d', 'e', 'l', 0); break;
case ysfx_key_f1: jsfx_key = key_c('f', '1', 0, 0); break;
case ysfx_key_f2: jsfx_key = key_c('f', '2', 0, 0); break;
case ysfx_key_f3: jsfx_key = key_c('f', '3', 0, 0); break;
case ysfx_key_f4: jsfx_key = key_c('f', '4', 0, 0); break;
case ysfx_key_f5: jsfx_key = key_c('f', '5', 0, 0); break;
case ysfx_key_f6: jsfx_key = key_c('f', '6', 0, 0); break;
case ysfx_key_f7: jsfx_key = key_c('f', '7', 0, 0); break;
case ysfx_key_f8: jsfx_key = key_c('f', '8', 0, 0); break;
case ysfx_key_f9: jsfx_key = key_c('f', '9', 0, 0); break;
case ysfx_key_f10: jsfx_key = key_c('f', '1', '0', 0); break;
case ysfx_key_f11: jsfx_key = key_c('f', '1', '1', 0); break;
case ysfx_key_f12: jsfx_key = key_c('f', '1', '2', 0); break;
case ysfx_key_left: jsfx_key = key_c('l', 'e', 'f', 't'); break;
case ysfx_key_up: jsfx_key = key_c('u', 'p', 0, 0); break;
case ysfx_key_right: jsfx_key = key_c('r', 'g', 'h', 't'); break;
case ysfx_key_down: jsfx_key = key_c('d', 'o', 'w', 'n'); break;
case ysfx_key_page_up: jsfx_key = key_c('p', 'g', 'u', 'p'); break;
case ysfx_key_page_down: jsfx_key = key_c('p', 'g', 'd', 'n'); break;
case ysfx_key_home: jsfx_key = key_c('h', 'o', 'm', 'e'); break;
case ysfx_key_end: jsfx_key = key_c('e', 'n', 'd', 0); break;
case ysfx_key_insert: jsfx_key = key_c('i', 'n', 's', 0); break;
}

return true;
}

static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_getchar(void *opaque, EEL_F *p)
{
ysfx_gfx_state_t *state = GFX_GET_CONTEXT(opaque);
if (!state)
return 0;

if (*p >= 1/*2*/) { // NOTE(jpc) this is 2.0 originally, which seems wrong
if (*p == 65536) {
// TODO implement window flags
return 0;
}

// current key down status
uint32_t key = (uint32_t)*p;
uint32_t key_id;
if (translate_special_key(key, key))
key_id = key;
else if (key < 256)
key_id = ysfx::latin1_tolower(key);
else // support the Latin-1 character set only
return 0;
return (EEL_F)(state->keys_pressed.find(key_id) != state->keys_pressed.end());
}

if (!state->input_queue.empty()) {
uint32_t key = state->input_queue.front();
state->input_queue.pop();
return (EEL_F)key;
}

return 0;
}

static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_showmenu(void *opaque, INT_PTR nparms, EEL_F **parms)
{
ysfx_gfx_state_t *state = GFX_GET_CONTEXT(opaque);
if (!state || !state->show_menu)
return 0;

ysfx_t *fx = (ysfx_t *)state->lice->m_user_ctx;

std::string desc;
if (!ysfx_string_get(fx, *parms[0], desc) || desc.empty())
return 0;

int32_t x = (int32_t)*fx->var.gfx_x;
int32_t y = (int32_t)*fx->var.gfx_y;
return state->show_menu(state->callback_data, desc.c_str(), x, y);
}

static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_setcursor(void *opaque, INT_PTR nparms, EEL_F **parms)
{
ysfx_gfx_state_t *state = GFX_GET_CONTEXT(opaque);
if (!state || !state->set_cursor)
return 0;

int32_t id = (int32_t)*parms[0];
state->set_cursor(state->callback_data, id);
return 0;
}

static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_getdropfile(void *opaque, INT_PTR np, EEL_F **parms)
{
ysfx_gfx_state_t *state = GFX_GET_CONTEXT(opaque);
if (!state || !state->get_drop_file)
return 0;

const int32_t idx = (int)*parms[0];
if (idx < 0) {
state->get_drop_file(state->callback_data, -1);
return 0;
}

const char *file = state->get_drop_file(state->callback_data, idx);
if (!file)
return 0;

if (np > 1) {
ysfx_t *fx = (ysfx_t *)state->lice->m_user_ctx;
ysfx_string_set(fx, *parms[1], file);
}

return 1;
}

#endif

//------------------------------------------------------------------------------
#if !defined(YSFX_NO_GFX)
ysfx_gfx_state_t::ysfx_gfx_state_t(ysfx_t *fx)
: lice{new eel_lice_state{fx->vm.get(), fx, ysfx_gfx_max_images, ysfx_gfx_max_fonts}}
{
lice->m_framebuffer = new LICE_WrapperBitmap{nullptr, 0, 0, 0, false};
}

ysfx_gfx_state_t::~ysfx_gfx_state_t()
{
}
#endif

ysfx_gfx_state_t *ysfx_gfx_state_new(ysfx_t *fx)
{
return new ysfx_gfx_state_t{fx};
}

void ysfx_gfx_state_free(ysfx_gfx_state_t *state)
{
delete state;
}

void ysfx_gfx_state_set_bitmap(ysfx_gfx_state_t *state, uint8_t *data, uint32_t w, uint32_t h, uint32_t stride)
{
if (stride == 0)
stride = 4 * w;

eel_lice_state *lice = state->lice.get();

assert(stride % 4 == 0);
*static_cast<LICE_WrapperBitmap *>(lice->m_framebuffer) = LICE_WrapperBitmap{(LICE_pixel *)data, (int)w, (int)h, (int)(stride / 4), false};
}

void ysfx_gfx_state_set_scale_factor(ysfx_gfx_state_t *state, ysfx_real scale)
{
state->scale = scale;
}

void ysfx_gfx_state_set_callback_data(ysfx_gfx_state_t *state, void *callback_data)
{
state->callback_data = callback_data;
}

void ysfx_gfx_state_set_show_menu_callback(ysfx_gfx_state_t *state, int (*callback)(void *, const char *, int32_t, int32_t))
{
state->show_menu = callback;
}

void ysfx_gfx_state_set_set_cursor_callback(ysfx_gfx_state_t *state, void (*callback)(void *, int32_t))
{
state->set_cursor = callback;
}

void ysfx_gfx_state_set_get_drop_file_callback(ysfx_gfx_state_t *state, const char *(*callback)(void *, int32_t))
{
state->get_drop_file = callback;
}

bool ysfx_gfx_state_is_dirty(ysfx_gfx_state_t *state)
{
return state->lice->m_framebuffer_dirty;
}

void ysfx_gfx_state_add_key(ysfx_gfx_state_t *state, uint32_t mods, uint32_t key, bool press)
{
if (key < 1)
return;

uint32_t key_id;
if (translate_special_key(key, key))
key_id = key;
else if (key < 256)
key_id = ysfx::latin1_tolower(key);
else // support the Latin-1 character set only
return;

uint32_t key_with_mod = key;
if (key_id >= 'a' && key_id <= 'z') {
uint32_t off = (uint32_t)(key_id - 'a');
if (mods & (ysfx_mod_ctrl|ysfx_mod_alt))
key_with_mod = off + 257;
else if (mods & ysfx_mod_ctrl)
key_with_mod = off + 1;
else if (mods & ysfx_mod_alt)
key_with_mod = off + 321;
}

if (press && key_with_mod > 0) {
while (state->input_queue.size() >= ysfx_gfx_max_input)
state->input_queue.pop();
state->input_queue.push(key_with_mod);
}

if (press)
state->keys_pressed.insert(key_id);
else
state->keys_pressed.erase(key_id);
}

//------------------------------------------------------------------------------
void ysfx_gfx_enter(ysfx_t *fx, bool doinit)
{
fx->gfx.mutex.lock();
ysfx_gfx_state_t *state = fx->gfx.state.get();

if (doinit) {
if (fx->gfx.must_init.exchange(false, std::memory_order_acquire)) {
*fx->var.gfx_r = 1.0;
*fx->var.gfx_g = 1.0;
*fx->var.gfx_b = 1.0;
*fx->var.gfx_a = 1.0;
*fx->var.gfx_a2 = 1.0;
*fx->var.gfx_dest = -1.0;
*fx->var.mouse_wheel = 0.0;
*fx->var.mouse_hwheel = 0.0;
// NOTE the above are according to eel_lice.h `resetVarsToStock`
// it helps to reset a few more, especially for clearing
*fx->var.gfx_mode = 0;
*fx->var.gfx_clear = 0;
*fx->var.gfx_texth = 0;
*fx->var.mouse_cap = 0;

// reset key state
state->input_queue = {};
state->keys_pressed = {};

// reset lice
eel_lice_state *lice = state->lice.get();
LICE_WrapperBitmap framebuffer = *static_cast<LICE_WrapperBitmap *>(lice->m_framebuffer);
state->lice.reset();
lice = new eel_lice_state{fx->vm.get(), fx, ysfx_gfx_max_images, ysfx_gfx_max_fonts};
state->lice.reset(lice);
lice->m_framebuffer = new LICE_WrapperBitmap(framebuffer);

// load images from filenames
uint32_t numfiles = (uint32_t)fx->source.main->header.filenames.size();
for (uint32_t i = 0; i < numfiles; ++i)
lice->gfx_loadimg(fx, (int32_t)i, (EEL_F)i);

fx->gfx.ready = true;
}
}

ysfx_set_thread_id(ysfx_thread_id_gfx);
}

void ysfx_gfx_leave(ysfx_t *fx)
{
ysfx_set_thread_id(ysfx_thread_id_none);

fx->gfx.mutex.unlock();
}

ysfx_gfx_state_t *ysfx_gfx_get_context(ysfx_t *fx)
{
if (!fx)
return nullptr;

// NOTE: make sure that this will be used from the @gfx thread only
if (ysfx_get_thread_id() != ysfx_thread_id_gfx)
return nullptr;

return fx->gfx.state.get();
}

void ysfx_gfx_prepare(ysfx_t *fx)
{
ysfx_gfx_state_t *state = ysfx_gfx_get_context(fx);
eel_lice_state *lice = state->lice.get();

lice->m_framebuffer_dirty = false;

// set variables `gfx_w` and `gfx_h`
ysfx_real gfx_w = (ysfx_real)lice->m_framebuffer->getWidth();
ysfx_real gfx_h = (ysfx_real)lice->m_framebuffer->getHeight();
if (state->scale > 1.0) {
gfx_w *= state->scale;
gfx_h *= state->scale;
*fx->var.gfx_ext_retina = state->scale;
}
*fx->var.gfx_w = gfx_w;
*fx->var.gfx_h = gfx_h;
}
#endif

//------------------------------------------------------------------------------
void ysfx_api_init_gfx()
{
#if !defined(YSFX_NO_GFX)
lice_stb_install_loaders();
#endif

NSEEL_addfunc_retptr("gfx_lineto", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_lineto);
NSEEL_addfunc_retptr("gfx_lineto", 2, NSEEL_PProc_THIS, &ysfx_api_gfx_lineto2);
NSEEL_addfunc_retptr("gfx_rectto", 2, NSEEL_PProc_THIS, &ysfx_api_gfx_rectto);
NSEEL_addfunc_varparm("gfx_rect", 4, NSEEL_PProc_THIS, &ysfx_api_gfx_rect);
NSEEL_addfunc_varparm("gfx_line", 4, NSEEL_PProc_THIS, &ysfx_api_gfx_line);
NSEEL_addfunc_varparm("gfx_gradrect", 8, NSEEL_PProc_THIS, &ysfx_api_gfx_gradrect);
NSEEL_addfunc_varparm("gfx_muladdrect", 7, NSEEL_PProc_THIS, &ysfx_api_gfx_muladdrect);
NSEEL_addfunc_varparm("gfx_deltablit", 9, NSEEL_PProc_THIS, &ysfx_api_gfx_deltablit);
NSEEL_addfunc_exparms("gfx_transformblit", 8, NSEEL_PProc_THIS, &ysfx_api_gfx_transformblit);
NSEEL_addfunc_varparm("gfx_circle", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_circle);
NSEEL_addfunc_varparm("gfx_triangle", 6, NSEEL_PProc_THIS, &ysfx_api_gfx_triangle);
NSEEL_addfunc_varparm("gfx_roundrect", 5, NSEEL_PProc_THIS, &ysfx_api_gfx_roundrect);
NSEEL_addfunc_varparm("gfx_arc", 5, NSEEL_PProc_THIS, &ysfx_api_gfx_arc);
NSEEL_addfunc_retptr("gfx_blurto", 2, NSEEL_PProc_THIS, &ysfx_api_gfx_blurto);
NSEEL_addfunc_exparms("gfx_showmenu", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_showmenu);
NSEEL_addfunc_varparm("gfx_setcursor", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_setcursor);
NSEEL_addfunc_retptr("gfx_drawnumber", 2, NSEEL_PProc_THIS, &ysfx_api_gfx_drawnumber);
NSEEL_addfunc_retptr("gfx_drawchar", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_drawchar);
NSEEL_addfunc_varparm("gfx_drawstr", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_drawstr);
NSEEL_addfunc_retptr("gfx_measurestr", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_measurestr);
NSEEL_addfunc_retptr("gfx_measurechar", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_measurechar);
NSEEL_addfunc_varparm("gfx_printf", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_printf);
NSEEL_addfunc_retptr("gfx_setpixel", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_setpixel);
NSEEL_addfunc_retptr("gfx_getpixel", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_getpixel);
NSEEL_addfunc_retptr("gfx_getimgdim", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_getimgdim);
NSEEL_addfunc_retval("gfx_setimgdim", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_setimgdim);
NSEEL_addfunc_retval("gfx_loadimg", 2, NSEEL_PProc_THIS, &ysfx_api_gfx_loadimg);
NSEEL_addfunc_retptr("gfx_blit", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_blit);
NSEEL_addfunc_retptr("gfx_blitext", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_blitext);
NSEEL_addfunc_varparm("gfx_blit", 4, NSEEL_PProc_THIS, &ysfx_api_gfx_blit2);
NSEEL_addfunc_varparm("gfx_setfont", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_setfont);
NSEEL_addfunc_varparm("gfx_getfont", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_getfont);
NSEEL_addfunc_varparm("gfx_set", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_set);
NSEEL_addfunc_varparm("gfx_getdropfile", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_getdropfile);
NSEEL_addfunc_varparm("gfx_getsyscol", 0, NSEEL_PProc_THIS, &ysfx_api_gfx_getsyscol);
NSEEL_addfunc_retval("gfx_getchar", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_getchar);
}

//------------------------------------------------------------------------------
// SWELL helpers

#if !defined(YSFX_NO_GFX)

// implement these GL functions on SWELL targets where they are missing

#if !defined(SWELL_TARGET_GDK) && !defined(SWELL_TARGET_OSX)
void SWELL_SetViewGL(HWND h, char wantGL)
{
}

bool SWELL_GetViewGL(HWND h)
{
return false;
}

bool SWELL_SetGLContextToView(HWND h)
{
return false;
}

#endif // !defined(SWELL_TARGET_GDK) && !defined(SWELL_TARGET_OSX)

#endif

+ 50
- 0
source/modules/ysfx/sources/ysfx_api_gfx.hpp View File

@@ -0,0 +1,50 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#pragma once
#include "ysfx.h"

#if !defined(YSFX_NO_GFX)
struct ysfx_gfx_state_t;
ysfx_gfx_state_t *ysfx_gfx_state_new(ysfx_t *fx);
void ysfx_gfx_state_free(ysfx_gfx_state_t *state);
YSFX_DEFINE_AUTO_PTR(ysfx_gfx_state_u, ysfx_gfx_state_t, ysfx_gfx_state_free);
void ysfx_gfx_state_set_bitmap(ysfx_gfx_state_t *state, uint8_t *data, uint32_t w, uint32_t h, uint32_t stride);
void ysfx_gfx_state_set_scale_factor(ysfx_gfx_state_t *state, ysfx_real scale);
void ysfx_gfx_state_set_callback_data(ysfx_gfx_state_t *state, void *callback_data);
void ysfx_gfx_state_set_show_menu_callback(ysfx_gfx_state_t *state, int (*callback)(void *, const char *, int32_t, int32_t));
void ysfx_gfx_state_set_set_cursor_callback(ysfx_gfx_state_t *state, void (*callback)(void *, int32_t));
void ysfx_gfx_state_set_get_drop_file_callback(ysfx_gfx_state_t *state, const char *(*callback)(void *, int32_t));
bool ysfx_gfx_state_is_dirty(ysfx_gfx_state_t *state);
void ysfx_gfx_state_add_key(ysfx_gfx_state_t *state, uint32_t mods, uint32_t key, bool press);
void ysfx_gfx_state_update_mouse(ysfx_gfx_state_t *state, uint32_t mods, int xpos, int ypos, uint32_t buttons, int wheel, int hwheel);

//------------------------------------------------------------------------------
void ysfx_gfx_enter(ysfx_t *fx, bool doinit);
void ysfx_gfx_leave(ysfx_t *fx);
ysfx_gfx_state_t *ysfx_gfx_get_context(ysfx_t *fx);
void ysfx_gfx_prepare(ysfx_t *fx);

struct ysfx_scoped_gfx_t {
ysfx_scoped_gfx_t(ysfx_t *fx, bool doinit) : m_fx(fx) { ysfx_gfx_enter(fx, doinit); }
~ysfx_scoped_gfx_t() { ysfx_gfx_leave(m_fx); }
ysfx_t *m_fx = nullptr;
};
#endif

//------------------------------------------------------------------------------
void ysfx_api_init_gfx();

+ 91
- 0
source/modules/ysfx/sources/ysfx_api_gfx_dummy.hpp View File

@@ -0,0 +1,91 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#pragma once
#include "ysfx_eel_utils.hpp"

static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_stub_varparm(void *, INT_PTR, EEL_F **)
{
return 0;
}

static EEL_F *NSEEL_CGEN_CALL ysfx_api_gfx_stub_retptr1(void *, EEL_F *arg)
{
return arg;
}

static EEL_F *NSEEL_CGEN_CALL ysfx_api_gfx_stub_retptr2(void *, EEL_F *arg, EEL_F *)
{
return arg;
}

static EEL_F *NSEEL_CGEN_CALL ysfx_api_gfx_stub_retptr3(void *, EEL_F *arg, EEL_F *, EEL_F *)
{
return arg;
}

static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_stub_retval1(void *, EEL_F *)
{
return 0;
}

static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_stub_retval2(void *, EEL_F *, EEL_F *)
{
return 0;
}

static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_stub_retval3(void *, EEL_F *, EEL_F *, EEL_F *)
{
return 0;
}

#define ysfx_api_gfx_lineto ysfx_api_gfx_stub_retptr3
#define ysfx_api_gfx_lineto2 ysfx_api_gfx_stub_retptr2
#define ysfx_api_gfx_rectto ysfx_api_gfx_stub_retptr2
#define ysfx_api_gfx_rect ysfx_api_gfx_stub_varparm
#define ysfx_api_gfx_line ysfx_api_gfx_stub_varparm
#define ysfx_api_gfx_gradrect ysfx_api_gfx_stub_varparm
#define ysfx_api_gfx_muladdrect ysfx_api_gfx_stub_varparm
#define ysfx_api_gfx_deltablit ysfx_api_gfx_stub_varparm
#define ysfx_api_gfx_transformblit ysfx_api_gfx_stub_varparm
#define ysfx_api_gfx_circle ysfx_api_gfx_stub_varparm
#define ysfx_api_gfx_triangle ysfx_api_gfx_stub_varparm
#define ysfx_api_gfx_roundrect ysfx_api_gfx_stub_varparm
#define ysfx_api_gfx_arc ysfx_api_gfx_stub_varparm
#define ysfx_api_gfx_blurto ysfx_api_gfx_stub_retptr2
#define ysfx_api_gfx_showmenu ysfx_api_gfx_stub_varparm
#define ysfx_api_gfx_setcursor ysfx_api_gfx_stub_varparm
#define ysfx_api_gfx_drawnumber ysfx_api_gfx_stub_retptr2
#define ysfx_api_gfx_drawchar ysfx_api_gfx_stub_retptr1
#define ysfx_api_gfx_drawstr ysfx_api_gfx_stub_varparm
#define ysfx_api_gfx_measurestr ysfx_api_gfx_stub_retptr3
#define ysfx_api_gfx_measurechar ysfx_api_gfx_stub_retptr3
#define ysfx_api_gfx_printf ysfx_api_gfx_stub_varparm
#define ysfx_api_gfx_setpixel ysfx_api_gfx_stub_retptr3
#define ysfx_api_gfx_getpixel ysfx_api_gfx_stub_retptr3
#define ysfx_api_gfx_getimgdim ysfx_api_gfx_stub_retptr3
#define ysfx_api_gfx_setimgdim ysfx_api_gfx_stub_retval3
#define ysfx_api_gfx_loadimg ysfx_api_gfx_stub_retval2
#define ysfx_api_gfx_blit ysfx_api_gfx_stub_retptr3
#define ysfx_api_gfx_blitext ysfx_api_gfx_stub_retptr3
#define ysfx_api_gfx_blit2 ysfx_api_gfx_stub_varparm
#define ysfx_api_gfx_setfont ysfx_api_gfx_stub_varparm
#define ysfx_api_gfx_getfont ysfx_api_gfx_stub_varparm
#define ysfx_api_gfx_set ysfx_api_gfx_stub_varparm
#define ysfx_api_gfx_getdropfile ysfx_api_gfx_stub_varparm
#define ysfx_api_gfx_getsyscol ysfx_api_gfx_stub_varparm
#define ysfx_api_gfx_getchar ysfx_api_gfx_stub_retval1

+ 1490
- 0
source/modules/ysfx/sources/ysfx_api_gfx_lice.hpp
File diff suppressed because it is too large
View File


+ 439
- 0
source/modules/ysfx/sources/ysfx_api_reaper.cpp View File

@@ -0,0 +1,439 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#include "ysfx.hpp"
#include "ysfx_api_reaper.hpp"
#include "ysfx_api_eel.hpp"
#include "ysfx_eel_utils.hpp"
#include "ysfx_utils.hpp"
#include <cmath>
#include <cstring>
#include <cassert>

#include "WDL/wdlstring.h"

#define REAPER_GET_INTERFACE(opaque) ((opaque) ? (ysfx_t *)(opaque) : nullptr)

static EEL_F *NSEEL_CGEN_CALL ysfx_api_spl(void *opaque, EEL_F *n_)
{
//NOTE: callable from @gfx thread

ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
int32_t n = ysfx_eel_round<int32_t>(*n_);

if (n < 0 || n >= ysfx_max_channels) {
fx->var.ret_temp = 0;
return &fx->var.ret_temp;
}

return fx->var.spl[(uint32_t)n];
}

static EEL_F *NSEEL_CGEN_CALL ysfx_api_slider(void *opaque, EEL_F *n_)
{
//NOTE: callable from @gfx thread

ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
int32_t n = ysfx_eel_round<int32_t>(*n_);

if (n < 1 || n > ysfx_max_sliders) {
fx->var.ret_temp = 0;
return &fx->var.ret_temp;
}

n -= 1;
return fx->var.slider[(uint32_t)n];
}

static EEL_F NSEEL_CGEN_CALL ysfx_api_slider_next_chg(void *opaque, EEL_F *index_, EEL_F *val_)
{
//TODO frame-accurate slider changes
(void)opaque;
(void)index_;
(void)val_;
return -1;
}

static EEL_F NSEEL_CGEN_CALL ysfx_api_slider_automate(void *opaque, EEL_F *mask_or_slider_)
{
//NOTE: callable from @gfx thread

ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
uint32_t slider = ysfx_get_slider_of_var(fx, mask_or_slider_);

uint64_t mask;
if (slider < ysfx_max_sliders)
mask = (uint64_t)1 << slider;
else
mask = ysfx_eel_round<uint64_t>(std::fabs(*mask_or_slider_));

fx->slider.automate_mask |= mask;
fx->slider.change_mask |= mask;
return 0;
}

static EEL_F NSEEL_CGEN_CALL ysfx_api_sliderchange(void *opaque, EEL_F *mask_or_slider_)
{
//NOTE: callable from @gfx thread

ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
uint32_t slider = ysfx_get_slider_of_var(fx, mask_or_slider_);

uint64_t mask;
if (slider < ysfx_max_sliders)
mask = (uint64_t)1 << slider;
else
mask = ysfx_eel_round<uint64_t>(std::fabs(*mask_or_slider_));

fx->slider.change_mask |= mask;
return 0;
}

static EEL_F NSEEL_CGEN_CALL ysfx_api_slider_show(void *opaque, EEL_F *mask_or_slider_, EEL_F *value_)
{
//NOTE: callable from @gfx thread

ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
uint32_t slider = ysfx_get_slider_of_var(fx, mask_or_slider_);

uint64_t mask;
if (slider < ysfx_max_sliders)
mask = (uint64_t)1 << slider;
else
mask = ysfx_eel_round<uint64_t>(std::fabs(*mask_or_slider_));

if (*value_ >= (EEL_F)+0.5) {
// show
fx->slider.visible_mask |= mask;
}
else if (*value_ >= (EEL_F)-0.5) {
// hide
mask = ~mask;
fx->slider.visible_mask &= mask;
}
else {
// toggle
mask = fx->slider.visible_mask.fetch_xor(mask) ^ mask;
}

return (EEL_F)mask;
}

static EEL_F NSEEL_CGEN_CALL ysfx_api_midisend(void *opaque, INT_PTR np, EEL_F **parms)
{
if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
return 0;

int32_t offset;
uint8_t msg1;
uint8_t msg2;
uint8_t msg3;

switch (np) {
case 3:
{
offset = ysfx_eel_round<int32_t>(*parms[0]);
msg1 = (uint8_t)ysfx_eel_round<int32_t>(*parms[1]);
const uint32_t msg23 = ysfx_eel_round<int32_t>(*parms[2]);
msg2 = (uint8_t)(msg23 & 0xff);
msg3 = (uint8_t)(msg23 >> 8);
break;
}
case 4:
offset = ysfx_eel_round<int32_t>(*parms[0]);
msg1 = (uint8_t)ysfx_eel_round<int32_t>(*parms[1]);
msg2 = (uint8_t)ysfx_eel_round<int32_t>(*parms[2]);
msg3 = (uint8_t)ysfx_eel_round<int32_t>(*parms[3]);
break;
default:
assert(false);
return 0;
}

if (offset < 0)
offset = 0;

// NOTE(jpc) correct the length of the message
// in case it should be less than 3 bytes
uint32_t length = ysfx_midi_sizeof(msg1);
if (length == 0) // don't know what message this is
length = 3;

ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
ysfx_midi_event_t event;
const uint8_t data[] = {msg1, msg2, msg3};
event.bus = ysfx_current_midi_bus(fx);
event.offset = (uint32_t)offset;
event.size = length;
event.data = data;
if (!ysfx_midi_push(fx->midi.out.get(), &event))
return 0;

return msg1;
}

static EEL_F NSEEL_CGEN_CALL ysfx_api_midisend_buf(void *opaque, EEL_F *offset_, EEL_F *buf_, EEL_F *len_)
{
if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
return 0;

int32_t offset = ysfx_eel_round<int32_t>(*offset_);
int32_t buf = ysfx_eel_round<int32_t>(*buf_);
int32_t len = ysfx_eel_round<int32_t>(*len_);

if (len <= 0)
return 0;
if (offset < 0)
offset = 0;

ysfx_t *fx = REAPER_GET_INTERFACE(opaque);

ysfx_midi_push_t mp;
if (!ysfx_midi_push_begin(fx->midi.out.get(), ysfx_current_midi_bus(fx), (uint32_t)offset, &mp))
return 0;

ysfx_eel_ram_reader reader{fx->vm.get(), buf};
for (uint32_t i = 0; i < (uint32_t)len; ++i) {
uint8_t byte = (uint8_t)ysfx_eel_round<int32_t>(reader.read_next());
if (!ysfx_midi_push_data(&mp, &byte, 1))
break;
}

if (!ysfx_midi_push_end(&mp))
return 0;

return len;
}

static EEL_F NSEEL_CGEN_CALL ysfx_api_midisend_str(void *opaque, EEL_F *offset_, EEL_F *str_)
{
if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
return 0;

int32_t offset = ysfx_eel_round<int32_t>(*offset_);

if (offset < 0)
offset = 0;

ysfx_t *fx = REAPER_GET_INTERFACE(opaque);

///
struct process_data {
ysfx_t *fx = nullptr;
uint32_t offset = 0;
uint32_t result = 0;
};

process_data pdata;
pdata.offset = (uint32_t)offset;
pdata.fx = fx;

auto process_str = [](void *userdata, WDL_FastString &str) {
process_data *pdata = (process_data *)userdata;
ysfx_t *fx = pdata->fx;
ysfx_midi_event_t event;
event.bus = ysfx_current_midi_bus(fx);
event.offset = pdata->offset;
event.size = (uint32_t)str.GetLength();
event.data = (const uint8_t *)str.Get();
pdata->result = ysfx_midi_push(fx->midi.out.get(), &event) ? event.size : 0;
};

///
if (!ysfx_string_access(fx, *str_, false, +process_str, &pdata))
return 0;

return pdata.result;
}

static EEL_F NSEEL_CGEN_CALL ysfx_api_midisyx(void *opaque, EEL_F *offset_, EEL_F *buf_, EEL_F *len_)
{
if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
return 0;

int32_t offset = ysfx_eel_round<int32_t>(*offset_);
int32_t buf = ysfx_eel_round<int32_t>(*buf_);
int32_t len = ysfx_eel_round<int32_t>(*len_);

if (len <= 0)
return 0;
if (offset < 0)
offset = 0;

ysfx_t *fx = REAPER_GET_INTERFACE(opaque);

ysfx_midi_push_t mp;
if (!ysfx_midi_push_begin(fx->midi.out.get(), ysfx_current_midi_bus(fx), (uint32_t)offset, &mp))
return 0;

ysfx_eel_ram_reader reader{fx->vm.get(), buf};
for (uint32_t i = 0; i < (uint32_t)len; ++i) {
uint8_t byte = (uint8_t)ysfx_eel_round<int32_t>(reader.read_next());
const uint8_t head = 0xf0;
const uint8_t tail = 0xf7;
if (i == 0 && byte != head) {
if (!ysfx_midi_push_data(&mp, &head, 1))
break;
}
if (!ysfx_midi_push_data(&mp, &byte, 1))
break;
if (i + 1 == (uint32_t)len && byte != tail) {
if (!ysfx_midi_push_data(&mp, &tail, 1))
break;
}
}

if (!ysfx_midi_push_end(&mp))
return 0;

return len;
}

static EEL_F NSEEL_CGEN_CALL ysfx_api_midirecv(void *opaque, INT_PTR np, EEL_F **parms)
{
if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
return 0;

ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
uint32_t bus = ysfx_current_midi_bus(fx);

ysfx_midi_event_t event;
bool have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
// pass through the sysex events
while (have_event && event.size > 3) {
ysfx_midi_push(fx->midi.out.get(), &event);
have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
}
if (!have_event)
return 0;

uint8_t msg1 = 0;
uint8_t msg2 = 0;
uint8_t msg3 = 0;

switch (event.size) {
case 3: msg3 = event.data[2]; // fall through
case 2: msg2 = event.data[1]; // fall through
case 1: msg1 = event.data[0]; break;
}

*parms[0] = (EEL_F)event.offset;
*parms[1] = (EEL_F)msg1;
switch (np) {
case 4:
*parms[2] = (EEL_F)msg2;
*parms[3] = (EEL_F)msg3;
break;
case 3:
*parms[2] = (EEL_F)(msg2 + (msg3 << 8));
break;
default:
assert(false);
return 0;
}

return 1;
}

static EEL_F NSEEL_CGEN_CALL ysfx_api_midirecv_buf(void *opaque, EEL_F *offset_, EEL_F *buf_, EEL_F *recvlen_)
{
if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
return 0;

ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
NSEEL_VMCTX vm = fx->vm.get();

int32_t buf = ysfx_eel_round<int32_t>(*buf_);
int32_t recvlen = ysfx_eel_round<int32_t>(*recvlen_);

if (recvlen < 0)
recvlen = 0;

uint32_t bus = ysfx_current_midi_bus(fx);

ysfx_midi_event_t event;
bool have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
// pass through the events larger than the buffer
while (have_event && event.size > (uint32_t)recvlen) {
ysfx_midi_push(fx->midi.out.get(), &event);
have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
}
if (!have_event)
return 0;

*offset_ = (EEL_F)event.offset;

ysfx_eel_ram_writer writer{vm, buf};
for (uint32_t i = 0; i < event.size; ++i)
writer.write_next(event.data[i]);

return event.size;
}

static EEL_F NSEEL_CGEN_CALL ysfx_api_midirecv_str(void *opaque, EEL_F *offset_, EEL_F *str_)
{
if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
return 0;

ysfx_t *fx = REAPER_GET_INTERFACE(opaque);

uint32_t bus = ysfx_current_midi_bus(fx);

ysfx_midi_event_t event;
bool have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
// pass through the events larger than the maximum string
while (have_event && event.size > ysfx_string_max_length) {
ysfx_midi_push(fx->midi.out.get(), &event);
have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
}
if (!have_event)
return 0;

auto process_str = [](void *userdata, WDL_FastString &str) {
ysfx_midi_event_t *event = (ysfx_midi_event_t *)userdata;
str.SetRaw((const char *)event->data, (int32_t)event->size);
};

///
if (!ysfx_string_access(fx, *str_, true, +process_str, &event))
return 0;

*offset_ = (EEL_F)event.offset;
return event.size;
}

//------------------------------------------------------------------------------
void ysfx_api_init_reaper()
{
NSEEL_addfunc_retptr("spl", 1, NSEEL_PProc_THIS, &ysfx_api_spl);
NSEEL_addfunc_retptr("slider", 1, NSEEL_PProc_THIS, &ysfx_api_slider);

NSEEL_addfunc_retval("slider_next_chg", 2, NSEEL_PProc_THIS, &ysfx_api_slider_next_chg);
NSEEL_addfunc_retval("slider_automate", 1, NSEEL_PProc_THIS, &ysfx_api_slider_automate);
NSEEL_addfunc_retval("sliderchange", 1, NSEEL_PProc_THIS, &ysfx_api_sliderchange);
NSEEL_addfunc_retval("slider_show", 2, NSEEL_PProc_THIS, &ysfx_api_slider_show);

NSEEL_addfunc_exparms("midisend", 3, NSEEL_PProc_THIS, &ysfx_api_midisend);
NSEEL_addfunc_exparms("midisend", 4, NSEEL_PProc_THIS, &ysfx_api_midisend);
NSEEL_addfunc_retval("midisend_buf", 3, NSEEL_PProc_THIS, &ysfx_api_midisend_buf);
NSEEL_addfunc_retval("midisend_str", 2, NSEEL_PProc_THIS, &ysfx_api_midisend_str);
NSEEL_addfunc_exparms("midirecv", 3, NSEEL_PProc_THIS, &ysfx_api_midirecv);
NSEEL_addfunc_exparms("midirecv", 4, NSEEL_PProc_THIS, &ysfx_api_midirecv);
NSEEL_addfunc_retval("midirecv_buf", 3, NSEEL_PProc_THIS, &ysfx_api_midirecv_buf);
NSEEL_addfunc_retval("midirecv_str", 2, NSEEL_PProc_THIS, &ysfx_api_midirecv_str);
NSEEL_addfunc_retval("midisyx", 3, NSEEL_PProc_THIS, &ysfx_api_midisyx);
}

+ 20
- 0
source/modules/ysfx/sources/ysfx_api_reaper.hpp View File

@@ -0,0 +1,20 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#pragma once

void ysfx_api_init_reaper();

+ 166
- 0
source/modules/ysfx/sources/ysfx_audio_flac.cpp View File

@@ -0,0 +1,166 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#include "ysfx_audio_flac.hpp"
#include "ysfx_utils.hpp"
#include <memory>
#include <cstring>

#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunused-function"
#endif

#define DR_FLAC_IMPLEMENTATION
#define DRFLAC_API static
#define DRFLAC_PRIVATE static
#include "dr_flac.h"

#if defined(__GNUC__)
# pragma GCC diagnostic pop
#endif

struct drflac_u_deleter {
void operator()(drflac *x) const noexcept { drflac_close(x); }
};
using drflac_u = std::unique_ptr<drflac, drflac_u_deleter>;

///
struct ysfx_flac_reader_t {
drflac_u flac;
uint32_t nbuff = 0;
std::unique_ptr<float[]> buff;
};

static bool ysfx_flac_can_handle(const char *path)
{
return ysfx::path_has_suffix(path, "flac");
}

static ysfx_audio_reader_t *ysfx_flac_open(const char *path)
{
#if !defined(_WIN32)
drflac_u flac{drflac_open_file(path, NULL)};
#else
drflac_u flac{drflac_open_file_w(ysfx::widen(path).c_str(), NULL)};
#endif
if (!flac)
return nullptr;
std::unique_ptr<ysfx_flac_reader_t> reader{new ysfx_flac_reader_t};
reader->flac = std::move(flac);
reader->buff.reset(new float[reader->flac->channels]);
return (ysfx_audio_reader_t *)reader.release();
}

static void ysfx_flac_close(ysfx_audio_reader_t *reader_)
{
ysfx_flac_reader_t *reader = (ysfx_flac_reader_t *)reader_;
delete reader;
}

static ysfx_audio_file_info_t ysfx_flac_info(ysfx_audio_reader_t *reader_)
{
ysfx_flac_reader_t *reader = (ysfx_flac_reader_t *)reader_;
ysfx_audio_file_info_t info;
info.channels = reader->flac->channels;
info.sample_rate = (ysfx_real)reader->flac->sampleRate;
return info;
}

static uint64_t ysfx_flac_avail(ysfx_audio_reader_t *reader_)
{
ysfx_flac_reader_t *reader = (ysfx_flac_reader_t *)reader_;
return reader->nbuff + reader->flac->channels * (reader->flac->totalPCMFrameCount - reader->flac->currentPCMFrame);
}

static void ysfx_flac_rewind(ysfx_audio_reader_t *reader_)
{
ysfx_flac_reader_t *reader = (ysfx_flac_reader_t *)reader_;
drflac_seek_to_pcm_frame(reader->flac.get(), 0);
reader->nbuff = 0;
}

static uint64_t ysfx_flac_unload_buffer(ysfx_audio_reader_t *reader_, ysfx_real *samples, uint64_t count)
{
ysfx_flac_reader_t *reader = (ysfx_flac_reader_t *)reader_;

uint32_t nbuff = reader->nbuff;
if (nbuff > count)
nbuff = (uint32_t)count;

if (nbuff == 0)
return 0;

const float *src = &reader->buff[reader->flac->channels - reader->nbuff];
for (uint32_t i = 0; i < nbuff; ++i)
samples[i] = src[i];

reader->nbuff -= nbuff;
return nbuff;
}

static uint64_t ysfx_flac_read(ysfx_audio_reader_t *reader_, ysfx_real *samples, uint64_t count)
{
ysfx_flac_reader_t *reader = (ysfx_flac_reader_t *)reader_;
uint32_t channels = reader->flac->channels;
uint64_t readtotal = 0;

if (count == 0)
return readtotal;
else {
uint64_t copied = ysfx_flac_unload_buffer(reader_, samples, count);
samples += copied;
count -= copied;
readtotal += copied;
}

if (count == 0)
return readtotal;
else {
float *f32buf = (float *)samples;
uint64_t readframes = drflac_read_pcm_frames_f32(reader->flac.get(), count / channels, f32buf);
uint64_t readsamples = channels * readframes;
// f32->f64
for (uint64_t i = readsamples; i-- > 0; )
samples[i] = f32buf[i];
samples += readsamples;
count -= readsamples;
readtotal += readsamples;
}

if (count == 0)
return readtotal;
else if (drflac_read_pcm_frames_f32(reader->flac.get(), 1, reader->buff.get()) == 1) {
reader->nbuff = channels;
uint64_t copied = ysfx_flac_unload_buffer(reader_, samples, count);
samples += copied;
count -= copied;
readtotal += copied;
}

return readtotal;
}

const ysfx_audio_format_t ysfx_audio_format_flac = {
&ysfx_flac_can_handle,
&ysfx_flac_open,
&ysfx_flac_close,
&ysfx_flac_info,
&ysfx_flac_avail,
&ysfx_flac_rewind,
&ysfx_flac_read,
};

+ 21
- 0
source/modules/ysfx/sources/ysfx_audio_flac.hpp View File

@@ -0,0 +1,21 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#pragma once
#include "ysfx.h"

extern const ysfx_audio_format_t ysfx_audio_format_flac;

+ 162
- 0
source/modules/ysfx/sources/ysfx_audio_wav.cpp View File

@@ -0,0 +1,162 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#include "ysfx_audio_wav.hpp"
#include "ysfx_utils.hpp"
#include <memory>
#include <cstring>

#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunused-function"
#endif

#define DR_WAV_IMPLEMENTATION
#define DRWAV_API static
#define DRWAV_PRIVATE static
#include "dr_wav.h"

#if defined(__GNUC__)
# pragma GCC diagnostic pop
#endif

struct ysfx_wav_reader_t {
~ysfx_wav_reader_t() { drwav_uninit(wav.get()); }
std::unique_ptr<drwav> wav;
uint32_t nbuff = 0;
std::unique_ptr<float[]> buff;
};

static bool ysfx_wav_can_handle(const char *path)
{
return ysfx::path_has_suffix(path, "wav");
}

static ysfx_audio_reader_t *ysfx_wav_open(const char *path)
{
std::unique_ptr<drwav> wav{new drwav};
#if !defined(_WIN32)
drwav_bool32 initok = drwav_init_file(wav.get(), path, nullptr);
#else
drwav_bool32 initok = drwav_init_file_w(wav.get(), ysfx::widen(path).c_str(), nullptr);
#endif
if (!initok)
return nullptr;
std::unique_ptr<ysfx_wav_reader_t> reader{new ysfx_wav_reader_t};
reader->wav = std::move(wav);
reader->buff.reset(new float[reader->wav->channels]);
return (ysfx_audio_reader_t *)reader.release();
}

static void ysfx_wav_close(ysfx_audio_reader_t *reader_)
{
ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_;
delete reader;
}

static ysfx_audio_file_info_t ysfx_wav_info(ysfx_audio_reader_t *reader_)
{
ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_;
ysfx_audio_file_info_t info;
info.channels = reader->wav->channels;
info.sample_rate = (ysfx_real)reader->wav->sampleRate;
return info;
}

static uint64_t ysfx_wav_avail(ysfx_audio_reader_t *reader_)
{
ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_;
return reader->nbuff + reader->wav->channels * (reader->wav->totalPCMFrameCount - reader->wav->readCursorInPCMFrames);
}

static void ysfx_wav_rewind(ysfx_audio_reader_t *reader_)
{
ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_;
drwav_seek_to_pcm_frame(reader->wav.get(), 0);
reader->nbuff = 0;
}

static uint64_t ysfx_wav_unload_buffer(ysfx_audio_reader_t *reader_, ysfx_real *samples, uint64_t count)
{
ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_;

uint32_t nbuff = reader->nbuff;
if (nbuff > count)
nbuff = (uint32_t)count;

if (nbuff == 0)
return 0;

const float *src = &reader->buff[reader->wav->channels - reader->nbuff];
for (uint32_t i = 0; i < nbuff; ++i)
samples[i] = src[i];

reader->nbuff -= nbuff;
return nbuff;
}

static uint64_t ysfx_wav_read(ysfx_audio_reader_t *reader_, ysfx_real *samples, uint64_t count)
{
ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_;
uint32_t channels = reader->wav->channels;
uint64_t readtotal = 0;

if (count == 0)
return readtotal;
else {
uint64_t copied = ysfx_wav_unload_buffer(reader_, samples, count);
samples += copied;
count -= copied;
readtotal += copied;
}

if (count == 0)
return readtotal;
else {
float *f32buf = (float *)samples;
uint64_t readframes = drwav_read_pcm_frames_f32(reader->wav.get(), count / channels, f32buf);
uint64_t readsamples = channels * readframes;
// f32->f64
for (uint64_t i = readsamples; i-- > 0; )
samples[i] = f32buf[i];
samples += readsamples;
count -= readsamples;
readtotal += readsamples;
}

if (count == 0)
return readtotal;
else if (drwav_read_pcm_frames_f32(reader->wav.get(), 1, reader->buff.get()) == 1) {
reader->nbuff = channels;
uint64_t copied = ysfx_wav_unload_buffer(reader_, samples, count);
samples += copied;
count -= copied;
readtotal += copied;
}

return readtotal;
}

const ysfx_audio_format_t ysfx_audio_format_wav = {
&ysfx_wav_can_handle,
&ysfx_wav_open,
&ysfx_wav_close,
&ysfx_wav_info,
&ysfx_wav_avail,
&ysfx_wav_rewind,
&ysfx_wav_read,
};

+ 21
- 0
source/modules/ysfx/sources/ysfx_audio_wav.hpp View File

@@ -0,0 +1,21 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#pragma once
#include "ysfx.h"

extern const ysfx_audio_format_t ysfx_audio_format_wav;

+ 159
- 0
source/modules/ysfx/sources/ysfx_config.cpp View File

@@ -0,0 +1,159 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#include "ysfx_config.hpp"
#include "ysfx_utils.hpp"
#include "ysfx_audio_wav.hpp"
#include "ysfx_audio_flac.hpp"
#include <cassert>

ysfx_config_t *ysfx_config_new()
{
return new ysfx_config_t;
}

void ysfx_config_free(ysfx_config_t *config)
{
if (!config)
return;

if (config->ref_count.fetch_sub(1, std::memory_order_acq_rel) == 1)
delete config;
}

void ysfx_config_add_ref(ysfx_config_t *config)
{
config->ref_count.fetch_add(1, std::memory_order_relaxed);
}

void ysfx_set_import_root(ysfx_config_t *config, const char *root)
{
config->import_root = ysfx::path_ensure_final_separator(root ? root : "");
}

void ysfx_set_data_root(ysfx_config_t *config, const char *root)
{
config->data_root = ysfx::path_ensure_final_separator(root ? root : "");
}

const char *ysfx_get_import_root(ysfx_config_t *config)
{
return config->import_root.c_str();
}

const char *ysfx_get_data_root(ysfx_config_t *config)
{
return config->data_root.c_str();
}

void ysfx_guess_file_roots(ysfx_config_t *config, const char *sourcepath)
{
if (config->import_root.empty()) {
bool stop = false;
const std::string sourcedir = ysfx::path_directory(sourcepath);

std::string cur_dir = sourcedir + "../";
ysfx::file_uid cur_uid;
if (!ysfx::get_file_uid(cur_dir.c_str(), cur_uid))
stop = true;

while (!stop) {
bool match =
ysfx::exists((cur_dir + "Effects/").c_str()) &&
ysfx::exists((cur_dir + "Data/").c_str());
if (match) {
stop = true;
config->import_root = cur_dir + "Effects/";
}
else {
cur_dir += "../";
ysfx::file_uid old_uid = cur_uid;
if (!ysfx::get_file_uid(cur_dir.c_str(), cur_uid) || old_uid == cur_uid)
stop = true;
}
}
}

if (config->data_root.empty() && !config->import_root.empty()) {
const std::string datadir = config->import_root + "../Data/";

bool match = ysfx::exists(datadir.c_str());
if (match)
config->data_root = datadir;
}
}

void ysfx_register_audio_format(ysfx_config_t *config, ysfx_audio_format_t *afmt)
{
config->audio_formats.push_back(*afmt);
}

void ysfx_register_builtin_audio_formats(ysfx_config_t *config)
{
config->audio_formats.push_back(ysfx_audio_format_wav);
config->audio_formats.push_back(ysfx_audio_format_flac);
}

void ysfx_set_log_reporter(ysfx_config_t *config, ysfx_log_reporter_t *reporter)
{
config->log_reporter = reporter;
}

void ysfx_set_user_data(ysfx_config_t *config, intptr_t userdata)
{
config->userdata = userdata;
}

//------------------------------------------------------------------------------
const char *ysfx_log_level_string(ysfx_log_level level)
{
switch (level) {
case ysfx_log_info:
return "info";
case ysfx_log_warning:
return "warning";
case ysfx_log_error:
return "error";
default:
assert(false);
return "?";
}
}

void ysfx_log(ysfx_config_t &conf, ysfx_log_level level, const char *message)
{
if (conf.log_reporter)
conf.log_reporter(conf.userdata, level, message);
else
fprintf(stderr, "[ysfx] %s: %s\n", ysfx_log_level_string(level), message);
}

void ysfx_logfv(ysfx_config_t &conf, ysfx_log_level level, const char *format, va_list ap)
{
char buf[256];
vsnprintf(buf, sizeof(buf), format, ap);
buf[sizeof(buf)-1] = '\0';
ysfx_log(conf, level, buf);
}

void ysfx_logf(ysfx_config_t &conf, ysfx_log_level level, const char *format, ...)
{
va_list ap;
va_start(ap, format);
ysfx_logfv(conf, level, format, ap);
va_end(ap);
}

+ 39
- 0
source/modules/ysfx/sources/ysfx_config.hpp View File

@@ -0,0 +1,39 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#pragma once
#include "ysfx.h"
#include <vector>
#include <string>
#include <atomic>
#include <cstdarg>

struct ysfx_config_s {
std::string import_root;
std::string data_root;
std::vector<ysfx_audio_format_t> audio_formats;
ysfx_log_reporter_t *log_reporter = nullptr;
intptr_t userdata = 0;
std::atomic<uint32_t> ref_count{1};
};

void ysfx_log(ysfx_config_t &conf, ysfx_log_level level, const char *message);
void ysfx_logfv(ysfx_config_t &conf, ysfx_log_level level, const char *format, va_list ap);
#if defined(__GNUC__)
__attribute__((format(printf, 3, 4)))
#endif
void ysfx_logf(ysfx_config_t &conf, ysfx_log_level level, const char *format, ...);

+ 66
- 0
source/modules/ysfx/sources/ysfx_eel_utils.cpp View File

@@ -0,0 +1,66 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#include "ysfx_eel_utils.hpp"

ysfx_eel_ram_reader::ysfx_eel_ram_reader(NSEEL_VMCTX vm, int64_t addr)
: m_vm(vm),
m_addr(addr)
{
}

EEL_F ysfx_eel_ram_reader::read_next()
{
if (m_block_avail == 0) {
m_block = (m_addr < 0 || m_addr > 0xFFFFFFFFu) ? nullptr :
NSEEL_VM_getramptr_noalloc(m_vm, (uint32_t)m_addr, (int32_t *)&m_block_avail);
if (m_block)
m_addr += m_block_avail;
else {
m_addr += 1;
m_block_avail = 1;
}
}
EEL_F value = m_block ? *m_block++ : 0;
m_block_avail -= 1;
return value;
}

//------------------------------------------------------------------------------
ysfx_eel_ram_writer::ysfx_eel_ram_writer(NSEEL_VMCTX vm, int64_t addr)
: m_vm(vm),
m_addr(addr)
{
}

bool ysfx_eel_ram_writer::write_next(EEL_F value)
{
if (m_block_avail == 0) {
m_block = (m_addr < 0 || m_addr > 0xFFFFFFFFu) ? nullptr :
NSEEL_VM_getramptr(m_vm, (uint32_t)m_addr, (int32_t *)&m_block_avail);
if (m_block)
m_addr += m_block_avail;
else {
m_addr += 1;
m_block_avail = 1;
}
}
if (m_block)
*m_block++ = value;
m_block_avail -= 1;
return true;
}

+ 57
- 0
source/modules/ysfx/sources/ysfx_eel_utils.hpp View File

@@ -0,0 +1,57 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#pragma once
#include "WDL/eel2/ns-eel.h"
#include "WDL/eel2/ns-eel-int.h"
#include <type_traits>
#include <cstdint>

template <class T>
inline typename std::enable_if<std::is_integral<T>::value, T>::type
ysfx_eel_round(EEL_F value)
{
return (T)(value + (EEL_F)0.0001); // same one as used in eel2 everywhere
}

//------------------------------------------------------------------------------
class ysfx_eel_ram_reader {
public:
ysfx_eel_ram_reader() = default;
ysfx_eel_ram_reader(NSEEL_VMCTX vm, int64_t addr);
EEL_F read_next();

private:
NSEEL_VMCTX m_vm{};
int64_t m_addr = 0;
const EEL_F *m_block = nullptr;
uint32_t m_block_avail = 0;
};

//------------------------------------------------------------------------------
class ysfx_eel_ram_writer {
public:
ysfx_eel_ram_writer() = default;
ysfx_eel_ram_writer(NSEEL_VMCTX vm, int64_t addr);
bool write_next(EEL_F value);

private:
NSEEL_VMCTX m_vm{};
int64_t m_addr = 0;
EEL_F *m_block = nullptr;
uint32_t m_block_avail = 0;
};

+ 214
- 0
source/modules/ysfx/sources/ysfx_midi.cpp View File

@@ -0,0 +1,214 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#include "ysfx_midi.hpp"
#include <cstring>
#include <cassert>

void ysfx_midi_reserve(ysfx_midi_buffer_t *midi, uint32_t capacity, bool extensible)
{
std::vector<uint8_t> data;
data.reserve(capacity);
std::swap(data, midi->data);
midi->extensible = extensible;
ysfx_midi_rewind(midi);
}

void ysfx_midi_clear(ysfx_midi_buffer_t *midi)
{
midi->data.clear();
ysfx_midi_rewind(midi);
}

bool ysfx_midi_push(ysfx_midi_buffer_t *midi, const ysfx_midi_event_t *event)
{
if (event->size > ysfx_midi_message_max_size)
return false;
if (event->bus >= ysfx_max_midi_buses)
return false;

ysfx_midi_header_t header;

if (!midi->extensible) {
size_t writable = midi->data.capacity() - midi->data.size();
if (writable < sizeof(header) + event->size)
return false;
}

const uint8_t *data = event->data;
const uint8_t *headp = (const uint8_t *)&header;
header.bus = event->bus;
header.offset = event->offset;
header.size = event->size;

midi->data.insert(midi->data.end(), headp, headp + sizeof(header));
midi->data.insert(midi->data.end(), data, data + header.size);
return true;
}

void ysfx_midi_rewind(ysfx_midi_buffer_t *midi)
{
midi->read_pos = 0;
for (uint32_t i = 0; i < ysfx_max_midi_buses; ++i)
midi->read_pos_for_bus[i] = 0;
}

bool ysfx_midi_get_next(ysfx_midi_buffer_t *midi, ysfx_midi_event_t *event)
{
size_t *pos_ptr = &midi->read_pos;
size_t pos = *pos_ptr;

size_t avail = midi->data.size() - pos;
ysfx_midi_header_t header;
if (avail == 0)
return false;

assert(avail >= sizeof(header));
memcpy(&header, &midi->data[pos], sizeof(header));
assert(avail >= sizeof(header) + header.size);

event->bus = header.bus;
event->offset = header.offset;
event->size = header.size;
event->data = &midi->data[pos + sizeof(header)];
*pos_ptr = pos + (sizeof(header) + header.size);
return true;
}

bool ysfx_midi_get_next_from_bus(ysfx_midi_buffer_t *midi, uint32_t bus, ysfx_midi_event_t *event)
{
if (bus >= ysfx_max_midi_buses)
return false;

size_t *pos_ptr = &midi->read_pos_for_bus[bus];
size_t pos = *pos_ptr;
size_t avail = midi->data.size() - pos;
ysfx_midi_header_t header;

bool found = false;
while (!found && avail > 0) {
assert(avail >= sizeof(header));
memcpy(&header, &midi->data[pos], sizeof(header));
assert(avail >= sizeof(header) + header.size);

found = header.bus == bus;
if (!found) {
pos += sizeof(header) + header.size;
avail -= sizeof(header) + header.size;
}
}

if (!found) {
*pos_ptr = pos;
return false;
}

event->bus = header.bus;
event->offset = header.offset;
event->size = header.size;
event->data = &midi->data[pos + sizeof(header)];
*pos_ptr = pos + (sizeof(header) + header.size);
return true;
}

bool ysfx_midi_push_begin(ysfx_midi_buffer_t *midi, uint32_t bus, uint32_t offset, ysfx_midi_push_t *mp)
{
ysfx_midi_header_t header;

mp->midi = midi;
mp->start = midi->data.size();
mp->count = 0;
mp->eob = false;

if (!midi->extensible) {
size_t writable = midi->data.capacity() - midi->data.size();
if (writable < sizeof(header)) {
mp->eob = true;
return false;
}
}

header.bus = bus;
header.offset = offset;
header.size = 0;

const uint8_t *headp = (const uint8_t *)&header;
midi->data.insert(midi->data.end(), headp, headp + sizeof(header));

return true;
}

bool ysfx_midi_push_data(ysfx_midi_push_t *mp, const uint8_t *data, uint32_t size)
{
if (mp->eob)
return false;

if (size > ysfx_midi_message_max_size || mp->count + size > ysfx_midi_message_max_size) {
mp->eob = true;
return false;
}

ysfx_midi_buffer_t *midi = mp->midi;

if (!midi->extensible) {
size_t writable = midi->data.capacity() - midi->data.size();
if (writable < size) {
mp->eob = true;
return false;
}
}

midi->data.insert(midi->data.end(), data, data + size);
mp->count += size;
return true;
}

bool ysfx_midi_push_end(ysfx_midi_push_t *mp)
{
if (mp->eob) {
mp->midi->data.resize(mp->start);
return false;
}

ysfx_midi_header_t header;
uint8_t *headp = &mp->midi->data[mp->start];
memcpy(&header, headp, sizeof(header));
header.size = mp->count;
memcpy(headp, &header, sizeof(header));
return true;
}

//------------------------------------------------------------------------------
uint32_t ysfx_midi_sizeof(uint8_t id)
{
if ((id >> 7) == 0) {
return 0;
}
else if ((id >> 4) != 0b1111) {
static const uint8_t sizetable[8] = {
3, 3, 3, 3, 2, 2, 3,
};
return sizetable[(id >> 4) & 0b111];
}
else {
static const uint8_t sizetable[16] = {
0, 2, 3, 2, 1, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1,
};
return sizetable[id & 0b1111];
}
}

+ 72
- 0
source/modules/ysfx/sources/ysfx_midi.hpp View File

@@ -0,0 +1,72 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#pragma once
#include "ysfx.h"
#include <vector>
#include <memory>

struct ysfx_midi_header_t {
uint32_t bus;
uint32_t offset;
uint32_t size;
};

struct ysfx_midi_buffer_t {
std::vector<uint8_t> data;
size_t read_pos = 0;
size_t read_pos_for_bus[ysfx_max_midi_buses] = {};
bool extensible = false;
};
using ysfx_midi_buffer_u = std::unique_ptr<ysfx_midi_buffer_t>;

enum {
ysfx_midi_message_max_size = 1 << 24,
};

// NOTE: regarding buses,
// The buffer keeps 2 kinds of read positions: global, and per-bus.
//
// These are tracked separately, so use either global/per-bus reading API,
// but not both mixed in the same piece of code.
//
// The JSFX API `midi*` implementations should always use per-bus access:
// if `ext_midi_bus` is true, use the bus defined by `midi_bus`, otherwise 0.

void ysfx_midi_reserve(ysfx_midi_buffer_t *midi, uint32_t capacity, bool extensible);
void ysfx_midi_clear(ysfx_midi_buffer_t *midi);
bool ysfx_midi_push(ysfx_midi_buffer_t *midi, const ysfx_midi_event_t *event);
void ysfx_midi_rewind(ysfx_midi_buffer_t *midi);
bool ysfx_midi_get_next(ysfx_midi_buffer_t *midi, ysfx_midi_event_t *event);
bool ysfx_midi_get_next_from_bus(ysfx_midi_buffer_t *midi, uint32_t bus, ysfx_midi_event_t *event);

// incremental writer into a midi buffer
struct ysfx_midi_push_t {
ysfx_midi_buffer_t *midi = nullptr;
size_t start = 0;
uint32_t count = 0;
bool eob = false;
};
bool ysfx_midi_push_begin(ysfx_midi_buffer_t *midi, uint32_t bus, uint32_t offset, ysfx_midi_push_t *mp);
bool ysfx_midi_push_data(ysfx_midi_push_t *mp, const uint8_t *data, uint32_t size);
bool ysfx_midi_push_end(ysfx_midi_push_t *mp);

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

// determine the length of a midi message according to its status byte
// if length is dynamic, returns 0
uint32_t ysfx_midi_sizeof(uint8_t id);

+ 380
- 0
source/modules/ysfx/sources/ysfx_parse.cpp View File

@@ -0,0 +1,380 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#include "ysfx_parse.hpp"
#include "ysfx_utils.hpp"
#include <cstdlib>
#include <cstring>

bool ysfx_parse_toplevel(ysfx::text_reader &reader, ysfx_toplevel_t &toplevel, ysfx_parse_error *error)
{
toplevel = ysfx_toplevel_t{};

ysfx_section_t *current = new ysfx_section_t;
toplevel.header.reset(current);

std::string line;
uint32_t lineno = 0;

line.reserve(256);

while (reader.read_next_line(line)) {
const char *linep = line.c_str();

if (linep[0] == '@') {
// a new section starts
ysfx::string_list tokens = ysfx::split_strings_noempty(linep, &ysfx::ascii_isspace);

current = new ysfx_section_t;

if (tokens[0] == "@init")
toplevel.init.reset(current);
else if (tokens[0] == "@slider")
toplevel.slider.reset(current);
else if (tokens[0] == "@block")
toplevel.block.reset(current);
else if (tokens[0] == "@sample")
toplevel.sample.reset(current);
else if (tokens[0] == "@serialize")
toplevel.serialize.reset(current);
else if (tokens[0] == "@gfx") {
toplevel.gfx.reset(current);
long gfx_w = 0;
long gfx_h = 0;
if (tokens.size() > 1)
gfx_w = (long)ysfx::dot_atof(tokens[1].c_str());
if (tokens.size() > 2)
gfx_h = (long)ysfx::dot_atof(tokens[2].c_str());
toplevel.gfx_w = (gfx_w > 0) ? (uint32_t)gfx_w : 0;
toplevel.gfx_h = (gfx_h > 0) ? (uint32_t)gfx_h : 0;
}
else {
delete current;
if (error) {
error->line = lineno;
error->message = std::string("Invalid section: ") + line;
}
return false;
}
current->line_offset = lineno + 1;
}
else {
current->text.append(line);
current->text.push_back('\n');
}

++lineno;
}

return true;
}

void ysfx_parse_header(ysfx_section_t *section, ysfx_header_t &header)
{
header = ysfx_header_t{};

ysfx::string_text_reader reader(section->text.c_str());

std::string line;
//uint32_t lineno = section->line_offset;

line.reserve(256);

///
auto unprefix = [](const char *text, const char **restp, const char *prefix) -> bool {
size_t len = strlen(prefix);
if (strncmp(text, prefix, len))
return false;
if (restp)
*restp = text + len;
return true;
};

//--------------------------------------------------------------------------
// pass 1: regular metadata

while (reader.read_next_line(line)) {
const char *linep = line.c_str();
const char *rest = nullptr;

ysfx_slider_t slider;
ysfx_parsed_filename_t filename;

///
if (unprefix(linep, &rest, "desc:")) {
if (header.desc.empty())
header.desc = ysfx::trim(rest, &ysfx::ascii_isspace);
}
else if (unprefix(linep, &rest, "author:")) {
if (header.author.empty())
header.author = ysfx::trim(rest, &ysfx::ascii_isspace);
}
else if (unprefix(linep, &rest, "tags:")) {
if (header.tags.empty()) {
for (const std::string &tag : ysfx::split_strings_noempty(rest, &ysfx::ascii_isspace))
header.tags.push_back(tag);
}
}
else if (unprefix(linep, &rest, "in_pin:")) {
header.explicit_pins = true;
header.in_pins.push_back(ysfx::trim(rest, &ysfx::ascii_isspace));
}
else if (unprefix(linep, &rest, "out_pin:")) {
header.explicit_pins = true;
header.out_pins.push_back(ysfx::trim(rest, &ysfx::ascii_isspace));
}
else if (unprefix(linep, &rest, "options:")) {
for (const std::string &opt : ysfx::split_strings_noempty(rest, &ysfx::ascii_isspace)) {
size_t pos = opt.find('=');
std::string name = (pos == opt.npos) ? opt : opt.substr(0, pos);
std::string value = (pos == opt.npos) ? std::string{} : opt.substr(pos + 1);
if (name == "gmem")
header.options.gmem = value;
else if (name == "maxmem") {
int32_t maxmem = (int32_t)ysfx::dot_atof(value.c_str());
header.options.maxmem = (maxmem < 0) ? 0 : (uint32_t)maxmem;
}
else if (name == "want_all_kb")
header.options.want_all_kb = true;
else if (name == "no_meter")
header.options.no_meter = true;
}
}
else if (unprefix(linep, &rest, "import") && ysfx::ascii_isspace(rest[0]))
header.imports.push_back(ysfx::trim(rest + 1, &ysfx::ascii_isspace));
else if (ysfx_parse_slider(linep, slider)) {
if (slider.id >= ysfx_max_sliders)
continue;
slider.exists = true;
header.sliders[slider.id] = slider;
}
else if (ysfx_parse_filename(linep, filename)) {
if (filename.index != header.filenames.size())
continue;
header.filenames.push_back(std::move(filename.filename));
}

//++lineno;
}

//--------------------------------------------------------------------------
// pass 2: comments

reader = ysfx::string_text_reader{section->text.c_str()};

while (reader.read_next_line(line)) {
const char *linep = line.c_str();
const char *rest = nullptr;

// some files contain metadata in the form of comments
// this is not part of spec, but we'll take this info regardless

if (unprefix(linep, &rest, "//author:")) {
if (header.author.empty())
header.author = ysfx::trim(rest, &ysfx::ascii_isspace);
}
else if (unprefix(linep, &rest, "//tags:")) {
if (header.tags.empty()) {
for (const std::string &tag : ysfx::split_strings_noempty(rest, &ysfx::ascii_isspace))
header.tags.push_back(tag);
}
}
}

//--------------------------------------------------------------------------
if (header.in_pins.size() == 1 && !ysfx::ascii_casecmp(header.in_pins.front().c_str(), "none"))
header.in_pins.clear();
if (header.out_pins.size() == 1 && !ysfx::ascii_casecmp(header.out_pins.front().c_str(), "none"))
header.out_pins.clear();

if (header.in_pins.size() > ysfx_max_channels)
header.in_pins.resize(ysfx_max_channels);
if (header.out_pins.size() > ysfx_max_channels)
header.out_pins.resize(ysfx_max_channels);
}

bool ysfx_parse_slider(const char *line, ysfx_slider_t &slider)
{
// NOTE this parser is intentionally very permissive,
// in order to match the reference behavior

slider = ysfx_slider_t{};

#define PARSE_FAIL do { \
/*fprintf(stderr, "parse error (line %d): `%s`\n", __LINE__, line);*/ \
return false; \
} while (0)

const char *cur = line;

for (const char *p = "slider"; *p; ++p) {
if (*cur++ != *p)
PARSE_FAIL;
}

// parse ID (1-index)
unsigned long id = strtoul(cur, (char **)&cur, 10);
if (id < 1 || id > ysfx_max_sliders)
PARSE_FAIL;
slider.id = (uint32_t)--id;

// semicolon
if (*cur++ != ':')
PARSE_FAIL;

// search if there is an '=' sign prior to any '<' or ','
// if there is, it's a custom variable
{
const char *pos = cur;
for (char c; pos && (c = *pos) && c != '='; )
pos = (c == '<' || c == ',') ? nullptr : (pos + 1);
if (pos && *pos) {
slider.var.assign(cur, pos);
cur = pos + 1;
}
else
slider.var = "slider" + std::to_string(id + 1);
}

// a regular slider
if (*cur != '/') {
slider.def = (ysfx_real)ysfx::dot_strtod(cur, (char **)&cur);

while (*cur && *cur != ',' && *cur != '<') ++cur;
if (!*cur) PARSE_FAIL;

// no range specification
if (*cur == ',')
++cur;
// range specification
else if (*cur == '<') {
++cur;

// min
slider.min = (ysfx_real)ysfx::dot_strtod(cur, (char **)&cur);

while (*cur && *cur != ',' && *cur != '>') ++cur;
if (!*cur) PARSE_FAIL;

// max
if (*cur == ',') {
++cur;
slider.max = (ysfx_real)ysfx::dot_strtod(cur, (char **)&cur);

while (*cur && *cur != ',' && *cur != '>') ++cur;
if (!*cur) PARSE_FAIL;
}

// inc
if (*cur == ',') {
++cur;
slider.inc = (ysfx_real)ysfx::dot_strtod(cur, (char **)&cur);

while (*cur && *cur != '{' && *cur != ',' && *cur != '>') ++cur;
if (!*cur) PARSE_FAIL;

// enumeration values
if (*cur == '{') {
const char *pos = ++cur;

while (*cur && *cur != '}' && *cur != '>') ++cur;
if (!*cur) PARSE_FAIL;

slider.is_enum = true;
slider.enum_names = ysfx::split_strings_noempty(
std::string(pos, cur).c_str(),
[](char c) -> bool { return c == ','; });
for (std::string &name : slider.enum_names)
name = ysfx::trim(name.c_str(), &ysfx::ascii_isspace);
}
}

while (*cur && *cur != '>') ++cur;
if (!*cur) PARSE_FAIL;

++cur;
}
else
PARSE_FAIL;

// NOTE: skip ',' and whitespace. not sure why, it's how it is
while (*cur && (*cur == ',' || ysfx::ascii_isspace(*cur))) ++cur;
if (!*cur) PARSE_FAIL;
}
// a path slider
else
{
const char *pos = cur;
while (*cur && *cur != ':') ++cur;
if (!*cur) PARSE_FAIL;

slider.path.assign(pos, cur);
++cur;
slider.def = (ysfx_real)ysfx::dot_strtod(cur, (char **)&cur);
slider.inc = 1;
slider.is_enum = true;

while (*cur && *cur != ':') ++cur;
if (!*cur) PARSE_FAIL;

++cur;
}

// description and visibility
while (ysfx::ascii_isspace(*cur))
++cur;

slider.initially_visible = true;
if (*cur == '-') {
++cur;
slider.initially_visible = false;
}

slider.desc = ysfx::trim(cur, &ysfx::ascii_isspace);
if (slider.desc.empty())
PARSE_FAIL;

//
#undef PARSE_FAIL

return true;
}

bool ysfx_parse_filename(const char *line, ysfx_parsed_filename_t &filename)
{
filename = ysfx_parsed_filename_t{};

const char *cur = line;

for (const char *p = "filename:"; *p; ++p) {
if (*cur++ != *p)
return false;
}

int64_t index = (int64_t)ysfx::dot_strtod(cur, (char **)&cur);
if (index < 0 || index > ~(uint32_t)0)
return false;

while (*cur && *cur != ',') ++cur;
if (!*cur) return false;;

++cur;

filename.index = (uint32_t)index;
filename.filename.assign(cur);
return true;
}

+ 101
- 0
source/modules/ysfx/sources/ysfx_parse.hpp View File

@@ -0,0 +1,101 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#pragma once
#include "ysfx.h"
#include "ysfx_reader.hpp"
#include "ysfx_utils.hpp"
#include <string>
#include <vector>
#include <cstdint>

struct ysfx_section_t;
struct ysfx_toplevel_t;
struct ysfx_slider_t;
struct ysfx_header_t;
using ysfx_section_u = std::unique_ptr<ysfx_section_t>;
using ysfx_toplevel_u = std::unique_ptr<ysfx_toplevel_t>;
using ysfx_slider_u = std::unique_ptr<ysfx_slider_t>;
using ysfx_header_u = std::unique_ptr<ysfx_header_t>;

struct ysfx_section_t {
uint32_t line_offset = 0;
std::string text;
};

struct ysfx_toplevel_t {
ysfx_section_u header;
ysfx_section_u init;
ysfx_section_u slider;
ysfx_section_u block;
ysfx_section_u sample;
ysfx_section_u serialize;
ysfx_section_u gfx;
uint32_t gfx_w = 0;
uint32_t gfx_h = 0;
};

struct ysfx_parse_error {
uint32_t line = 0;
std::string message;
explicit operator bool() { return !message.empty(); }
};

struct ysfx_slider_t {
uint32_t id = 0;
bool exists = false;
ysfx_real def = 0;
ysfx_real min = 0;
ysfx_real max = 0;
ysfx_real inc = 0;
std::string var;
std::string path;
bool is_enum = false;
ysfx::string_list enum_names;
std::string desc;
bool initially_visible = false;
};

struct ysfx_options_t {
std::string gmem;
uint32_t maxmem = 0;
bool want_all_kb = false;
bool no_meter = false;
};

struct ysfx_header_t {
std::string desc;
std::string author;
ysfx::string_list tags;
ysfx::string_list imports;
ysfx::string_list in_pins;
ysfx::string_list out_pins;
bool explicit_pins = false;
ysfx::string_list filenames;
ysfx_options_t options;
ysfx_slider_t sliders[ysfx_max_sliders];
};

struct ysfx_parsed_filename_t {
uint32_t index;
std::string filename;
};

bool ysfx_parse_toplevel(ysfx::text_reader &reader, ysfx_toplevel_t &toplevel, ysfx_parse_error *error);
bool ysfx_parse_slider(const char *line, ysfx_slider_t &slider);
bool ysfx_parse_filename(const char *line, ysfx_parsed_filename_t &filename);
void ysfx_parse_header(ysfx_section_t *section, ysfx_header_t &header);

+ 171
- 0
source/modules/ysfx/sources/ysfx_preset.cpp View File

@@ -0,0 +1,171 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#include "ysfx.h"
#include "ysfx_preset.hpp"
#include "ysfx_utils.hpp"
#include <vector>
#include <string>
#include <cstring>

#include "WDL/lineparse.h"

static void ysfx_preset_clear(ysfx_preset_t *preset);
static ysfx_bank_t *ysfx_load_bank_from_rpl_text(const std::string &text);
static void ysfx_parse_preset_from_rpl_blob(ysfx_preset_t *preset, const char *name, const std::vector<uint8_t> &data);

ysfx_bank_t *ysfx_load_bank(const char *path)
{
ysfx::FILE_u stream{fopen(path, "rb")};
if (!stream)
return nullptr;

std::string input;
constexpr uint32_t max_input = 1u << 24;
input.reserve(1u << 16);

for (int ch; input.size() < max_input && (ch = fgetc(stream.get())) != EOF; ) {
ch = (ch == '\r' || ch == '\n') ? ' ' : ch;
input.push_back((unsigned char)ch);
}

if (ferror(stream.get()))
return nullptr;

stream.reset();
return ysfx_load_bank_from_rpl_text(input);
}

void ysfx_bank_free(ysfx_bank_t *bank)
{
if (!bank)
return;

delete[] bank->name;

if (ysfx_preset_t *presets = bank->presets) {
uint32_t count = bank->preset_count;
for (uint32_t i = 0; i < count; ++i)
ysfx_preset_clear(&presets[i]);
delete[] presets;
}

delete bank;
}

static void ysfx_preset_clear(ysfx_preset_t *preset)
{
delete[] preset->name;
preset->name = nullptr;

ysfx_state_free(preset->state);
preset->state = nullptr;
}

static ysfx_bank_t *ysfx_load_bank_from_rpl_text(const std::string &text)
{
LineParser parser;
if (parser.parse(text.c_str()) < 0)
return nullptr;

///
std::vector<ysfx_preset_t> preset_list;
preset_list.reserve(256);

auto list_cleanup = ysfx::defer([&preset_list]() {
for (ysfx_preset_t &pst : preset_list)
ysfx_preset_clear(&pst);
});

///
int ntok = parser.getnumtokens();
int itok = 0;

if (strcmp("<REAPER_PRESET_LIBRARY", parser.gettoken_str(itok++)) != 0)
return nullptr;

const char *bank_name = parser.gettoken_str(itok++);

std::string base64;
base64.reserve(1024);

while (itok < ntok) {
if (strcmp("<PRESET", parser.gettoken_str(itok++)) == 0) {
const char *preset_name = parser.gettoken_str(itok++);

base64.clear();
for (const char *part; itok < ntok &&
strcmp(">", (part = parser.gettoken_str(itok++))) != 0; )
base64.append(part);

preset_list.emplace_back();
ysfx_preset_t &preset = preset_list.back();

ysfx_parse_preset_from_rpl_blob(
&preset, preset_name,
ysfx::decode_base64(base64.data(), base64.size()));
}
}

///
ysfx_bank_u bank{new ysfx_bank_t{}};
bank->name = ysfx::strdup_using_new(bank_name);
bank->presets = new ysfx_preset_t[(uint32_t)preset_list.size()]{};
bank->preset_count = (uint32_t)preset_list.size();

for (uint32_t i = (uint32_t)preset_list.size(); i-- > 0; ) {
bank->presets[i] = preset_list[i];
preset_list.pop_back();
}

return bank.release();
}

static void ysfx_parse_preset_from_rpl_blob(ysfx_preset_t *preset, const char *name, const std::vector<uint8_t> &data)
{
ysfx_state_t state{};
std::vector<ysfx_state_slider_t> sliders;

size_t len = data.size();
size_t pos = 0;
while (pos < len && data[pos] != 0) ++pos;

if (pos++ < len) {
state.data = const_cast<uint8_t *>(&data[pos]);
state.data_size = len - pos;

LineParser parser;
if (parser.parse((const char *)data.data()) >= 0) {
sliders.reserve(ysfx_max_sliders);

for (uint32_t i = 0; i < 64; ++i) {
int success = false;
ysfx_state_slider_t slider{};
slider.index = i;
slider.value = (ysfx_real)parser.gettoken_float(i, &success);
if (success)
sliders.push_back(slider);
}

state.sliders = sliders.data();
state.slider_count = (uint32_t)sliders.size();
}
}

preset->name = ysfx::strdup_using_new(name);
preset->state = ysfx_state_dup(&state);
}

+ 18
- 0
source/modules/ysfx/sources/ysfx_preset.hpp View File

@@ -0,0 +1,18 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#pragma once

+ 98
- 0
source/modules/ysfx/sources/ysfx_reader.cpp View File

@@ -0,0 +1,98 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#include "ysfx_reader.hpp"

namespace ysfx {

//------------------------------------------------------------------------------
bool text_reader::read_next_line(std::string &line)
{
line.clear();

char next = read_next_char();
if (next == '\0')
return false;

while (next != '\0' && next != '\r' && next != '\n') {
line.push_back(next);
next = read_next_char();
}

if (next == '\r') {
next = peek_next_char();
if (next == '\n')
read_next_char();
}

return true;
}

//------------------------------------------------------------------------------
char string_text_reader::read_next_char()
{
const char *ptr = m_char_ptr;

if (!ptr || *ptr == '\0')
return '\0';

char next = *ptr;
m_char_ptr = ptr + 1;
return next;
}

char string_text_reader::peek_next_char()
{
const char *ptr = m_char_ptr;

if (!ptr)
return '\0';

return *ptr;
}

//------------------------------------------------------------------------------
char stdio_text_reader::read_next_char()
{
FILE *stream = m_stream;

if (!stream)
return '\0';

int next = fgetc(stream);
if (next == EOF)
return '\0';

return (unsigned char)next;
}

char stdio_text_reader::peek_next_char()
{
FILE *stream = m_stream;

if (!stream)
return '\0';

int next = fgetc(stream);
if (next == EOF)
return '\0';

ungetc(next, stream);
return (unsigned char)next;
}

} // namespace ysfx

+ 56
- 0
source/modules/ysfx/sources/ysfx_reader.hpp View File

@@ -0,0 +1,56 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#pragma once
#include <string>
#include <cstdio>
#include <cstddef>

namespace ysfx {

class text_reader
{
public:
virtual ~text_reader() = default;
virtual char read_next_char() = 0;
virtual char peek_next_char() = 0;
bool read_next_line(std::string &line);
};

//------------------------------------------------------------------------------
class string_text_reader : public text_reader
{
public:
explicit string_text_reader(const char *text) : m_char_ptr(text) {}
char read_next_char() override;
char peek_next_char() override;
private:
const char *m_char_ptr = nullptr;
};

//------------------------------------------------------------------------------
class stdio_text_reader : public text_reader
{
public:
explicit stdio_text_reader(FILE *stream) : m_stream(stream) {}
char read_next_char() override;
char peek_next_char() override;
private:
FILE *m_stream = nullptr;
};

} // namespace ysfx

+ 730
- 0
source/modules/ysfx/sources/ysfx_utils.cpp View File

@@ -0,0 +1,730 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#include "ysfx_utils.hpp"
#include "base64/Base64.hpp"
#include <system_error>
#include <algorithm>
#include <deque>
#include <clocale>
#include <cstring>
#include <cassert>
#if !defined(_WIN32)
# include <sys/stat.h>
# include <sys/types.h>
# include <unistd.h>
# include <dirent.h>
# include <fcntl.h>
#else
# include <windows.h>
# include <io.h>
#endif

namespace ysfx {

#if !defined(_WIN32)
static_assert(sizeof(off_t) == 8, "64-bit large file support is not enabled");
#endif

FILE *fopen_utf8(const char *path, const char *mode)
{
#if defined(_WIN32)
return _wfopen(widen(path).c_str(), widen(mode).c_str());
#else
return fopen(path, mode);
#endif
}

int64_t fseek_lfs(FILE *stream, int64_t off, int whence)
{
#if defined(_WIN32)
return _fseeki64(stream, off, whence);
#else
return fseeko(stream, off, whence);
#endif
}

int64_t ftell_lfs(FILE *stream)
{
#if defined(_WIN32)
return _ftelli64(stream);
#else
return ftello(stream);
#endif
}

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

namespace {

struct scoped_c_locale
{
scoped_c_locale(int lc, const char *name);
~scoped_c_locale();
c_locale_t m_loc{};

scoped_c_locale(const scoped_c_locale &) = delete;
scoped_c_locale &operator=(const scoped_c_locale &) = delete;
};

scoped_c_locale::scoped_c_locale(int lc, const char *name)
{
#if defined(_WIN32)
m_loc = _create_locale(lc, name);
#else
switch (lc) {
case LC_ALL:
m_loc = newlocale(LC_ALL_MASK, name, c_locale_t{});
break;
case LC_CTYPE:
m_loc = newlocale(LC_CTYPE_MASK, name, c_locale_t{});
break;
case LC_COLLATE:
m_loc = newlocale(LC_COLLATE_MASK, name, c_locale_t{});
break;
case LC_MONETARY:
m_loc = newlocale(LC_MONETARY_MASK, name, c_locale_t{});
break;
case LC_NUMERIC:
m_loc = newlocale(LC_NUMERIC_MASK, name, c_locale_t{});
break;
case LC_TIME:
m_loc = newlocale(LC_TIME_MASK, name, c_locale_t{});
break;
case LC_MESSAGES:
m_loc = newlocale(LC_MESSAGES_MASK, name, c_locale_t{});
break;
default:
errno = EINVAL;
break;
}
#endif
if (!m_loc)
throw std::system_error(errno, std::generic_category());
}

scoped_c_locale::~scoped_c_locale()
{
if (!m_loc) return;
#if !defined(_WIN32)
freelocale(m_loc);
#else
_free_locale(m_loc);
#endif
}

#if !defined(_WIN32)
struct scoped_posix_uselocale {
explicit scoped_posix_uselocale(c_locale_t loc);
~scoped_posix_uselocale();

c_locale_t m_loc{};
c_locale_t m_old{};

scoped_posix_uselocale(const scoped_posix_uselocale &) = delete;
scoped_posix_uselocale &operator=(const scoped_posix_uselocale &) = delete;
};

scoped_posix_uselocale::scoped_posix_uselocale(c_locale_t loc)
{
if (loc)
{
m_loc = loc;
m_old = uselocale(loc);
}
}

scoped_posix_uselocale::~scoped_posix_uselocale()
{
if (m_loc)
uselocale(m_old);
}
#endif

} // namespace

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

c_locale_t c_numeric_locale()
{
static scoped_c_locale loc(LC_NUMERIC, "C");
return loc.m_loc;
}

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

double c_atof(const char *text, c_locale_t loc)
{
#if defined(_WIN32)
return _atof_l(text, loc);
#else
scoped_posix_uselocale use(loc);
return atof(text);
#endif
}

double c_strtod(const char *text, char **endp, c_locale_t loc)
{
#if defined(_WIN32)
return _strtod_l(text, endp, loc);
#else
scoped_posix_uselocale use(loc);
return strtod(text, endp);
#endif
}

double dot_atof(const char *text)
{
return c_atof(text, c_numeric_locale());
}

double dot_strtod(const char *text, char **endp)
{
return c_strtod(text, endp, c_numeric_locale());
}

bool ascii_isspace(char c)
{
switch (c) {
case ' ': case '\f': case '\n': case '\r': case '\t': case '\v':
return true;
default:
return false;
}
}

bool ascii_isalpha(char c)
{
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}

char ascii_tolower(char c)
{
return (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c;
}

char ascii_toupper(char c)
{
return (c >= 'a' && c <= 'z') ? (c - 'a' + 'A') : c;
}

int ascii_casecmp(const char *a, const char *b)
{
for (char ca, cb; (ca = *a++) | (cb = *b++); ) {
ca = ascii_tolower(ca);
cb = ascii_tolower(cb);
if (ca < cb) return -1;
if (ca > cb) return +1;
}
return 0;
}

uint32_t latin1_toupper(uint32_t c)
{
if (c >= 'a' && c <= 'z')
return c - 'a' + 'A';
if ((c >= 0xe0 && c <= 0xf6) || (c >= 0xf8 && c <= 0xfe))
return c - 0x20;
return c;
}

uint32_t latin1_tolower(uint32_t c)
{
if (c >= 'A' && c <= 'Z')
return c - 'A' + 'a';
if ((c >= 0xc0 && c <= 0xd6) || (c >= 0xd8 && c <= 0xde))
return c + 0x20;
return c;
}

char *strdup_using_new(const char *src)
{
size_t len = strlen(src);
char *dst = new char[len + 1];
memcpy(dst, src, len + 1);
return dst;
}

string_list split_strings_noempty(const char *input, bool(*pred)(char))
{
string_list list;

if (input) {
std::string acc;
acc.reserve(256);

for (char c; (c = *input++) != '\0'; ) {
if (!pred(c))
acc.push_back(c);
else {
if (!acc.empty()) {
list.push_back(acc);
acc.clear();
}
}
}

if (!acc.empty())
list.push_back(acc);
}

return list;
}

std::string trim(const char *input, bool(*pred)(char))
{
const char *start = input;
while (*start != '\0' && pred(*start))
++start;

const char *end = start + strlen(start);
while (end > start && pred(*(end - 1)))
--end;

return std::string(start, end);
}

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

void pack_u32le(uint32_t value, uint8_t data[4])
{
data[0] = value & 0xff;
data[1] = (value >> 8) & 0xff;
data[2] = (value >> 16) & 0xff;
data[3] = value >> 24;
}

void pack_f32le(float value, uint8_t data[4])
{
uint32_t u;
memcpy(&u, &value, 4);
pack_u32le(u, data);
}

uint32_t unpack_u32le(const uint8_t data[4])
{
return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
}

float unpack_f32le(const uint8_t data[4])
{
float value;
uint32_t u = unpack_u32le(data);
memcpy(&value, &u, 4);
return value;
}

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

std::vector<uint8_t> decode_base64(const char *text, size_t len)
{
return d_getChunkFromBase64String(text, len);
}

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

bool get_file_uid(const char *path, file_uid &uid)
{
#ifdef _WIN32
HANDLE handle = CreateFileW(widen(path).c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
if (handle == INVALID_HANDLE_VALUE)
return false;
bool success = get_handle_file_uid((void *)handle, uid);
CloseHandle(handle);
return success;
#else
int fd = open(path, O_RDONLY);
if (fd == -1)
return false;
bool success = get_descriptor_file_uid(fd, uid);
close(fd);
return success;
#endif
}

bool get_stream_file_uid(FILE *stream, file_uid &uid)
{
#if !defined(_WIN32)
int fd = fileno(stream);
if (fd == -1)
return false;
#else
int fd = _fileno(stream);
if (fd == -1)
return false;
#endif
return get_descriptor_file_uid(fd, uid);
}

bool get_descriptor_file_uid(int fd, file_uid &uid)
{
#if !defined(_WIN32)
struct stat st;
if (fstat(fd, &st) != 0)
return false;
uid.first = (uint64_t)st.st_dev;
uid.second = (uint64_t)st.st_ino;
return true;
#else
HANDLE handle = (HANDLE)_get_osfhandle(fd);
if (handle == INVALID_HANDLE_VALUE)
return false;
return get_handle_file_uid((void *)handle, uid);
#endif
}

#if defined(_WIN32)
bool get_handle_file_uid(void *handle, file_uid &uid)
{
BY_HANDLE_FILE_INFORMATION info;
if (!GetFileInformationByHandle((HANDLE)handle, &info))
return false;
uid.first = info.dwVolumeSerialNumber;
uid.second = (uint64_t)info.nFileIndexLow | ((uint64_t)info.nFileIndexHigh << 32);
return true;
}
#endif

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

bool is_path_separator(char ch)
{
#if !defined(_WIN32)
return ch == '/';
#else
return ch == '/' || ch == '\\';
#endif
}

split_path_t split_path(const char *path)
{
split_path_t sp;
#if !defined(_WIN32)
size_t npos = ~(size_t)0;
size_t pos = npos;
for (size_t i = 0; path[i] != '\0'; ++i) {
if (is_path_separator(path[i]))
pos = i;
}
if (pos == npos)
sp.file.assign(path);
else {
sp.dir.assign(path, pos + 1);
sp.file.assign(path + pos + 1);
}
#else
std::wstring wpath = widen(path);
std::unique_ptr<wchar_t[]> drive{new wchar_t[wpath.size() + 1]{}};
std::unique_ptr<wchar_t[]> dir{new wchar_t[wpath.size() + 1]{}};
std::unique_ptr<wchar_t[]> fname{new wchar_t[wpath.size() + 1]{}};
std::unique_ptr<wchar_t[]> ext{new wchar_t[wpath.size() + 1]{}};
_wsplitpath(wpath.c_str(), drive.get(), dir.get(), fname.get(), ext.get());
sp.drive = narrow(drive.get());
if (!drive[0] || dir[0] == L'/' || dir[0] == L'\\')
sp.dir = narrow(dir.get());
else
sp.dir = narrow(L'\\' + std::wstring(dir.get()));
sp.file = narrow(std::wstring(fname.get()) + std::wstring(ext.get()));
#endif
return sp;
}

std::string path_file_name(const char *path)
{
return split_path(path).file;
}

std::string path_directory(const char *path)
{
split_path_t sp = split_path(path);
return sp.dir.empty() ? std::string("./") : (sp.drive + sp.dir);
}

std::string path_ensure_final_separator(const char *path)
{
std::string result(path);

if (!result.empty() && !is_path_separator(result.back()))
result.push_back('/');

return result;
}

bool path_has_suffix(const char *path, const char *suffix)
{
if (*suffix == '.')
++suffix;

size_t plen = strlen(path);
size_t slen = strlen(suffix);
if (plen < slen + 2)
return false;

return path[plen - slen - 1] == '.' &&
ascii_casecmp(suffix, &path[plen - slen]) == 0;
}

bool path_is_relative(const char *path)
{
#if !defined(_WIN32)
return !is_path_separator(path[0]);
#else
return !is_path_separator(split_path(path).dir.c_str()[0]);
#endif
}

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

#if !defined(_WIN32)
bool exists(const char *path)
{
return access(path, F_OK) == 0;
}

string_list list_directory(const char *path)
{
string_list list;

DIR *dir = opendir(path);
if (!dir)
return list;
auto dir_cleanup = defer([dir]() { closedir(dir); });

list.reserve(256);

std::string pathbuf;
pathbuf.reserve(1024);

while (dirent *ent = readdir(dir)) {
const char *name = ent->d_name;
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;

pathbuf.assign(name);
if (ent->d_type == DT_DIR)
pathbuf.push_back('/');

list.push_back(pathbuf);
}

std::sort(list.begin(), list.end());
return list;
}

// void visit_directories(const char *rootpath, bool (*visit)(const std::string &, void *), void *data);
// NOTE: implemented in separate file `ysfx_utils_fts.cpp`
#else
bool exists(const char *path)
{
return _waccess(widen(path).c_str(), 0) == 0;
}

string_list list_directory(const char *path)
{
string_list list;

std::wstring pattern = widen(path) + L"\\*";

WIN32_FIND_DATAW fd;
HANDLE handle = FindFirstFileW(pattern.c_str(), &fd);
if (handle == INVALID_HANDLE_VALUE)
return list;
auto handle_cleanup = defer([handle]() { FindClose(handle); });

list.reserve(256);

do {
const wchar_t *name = fd.cFileName;
if (!wcscmp(name, L".") || !wcscmp(name, L".."))
continue;

std::string entry = narrow(name);
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && !(fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
entry.push_back('/');

list.push_back(std::move(entry));
} while (FindNextFileW(handle, &fd));

std::sort(list.begin(), list.end());
return list;
}

void visit_directories(const char *rootpath, bool (*visit)(const std::string &, void *), void *data)
{
std::deque<std::wstring> dirs;
dirs.push_back(widen(path_ensure_final_separator(rootpath)));

std::wstring pathbuf;
pathbuf.reserve(1024);

std::vector<std::wstring> entries;
entries.reserve(256);

while (!dirs.empty()) {
std::wstring dir = std::move(dirs.front());
dirs.pop_front();

if (!visit(narrow(dir), data))
return;

pathbuf.assign(dir);
pathbuf.append(L"\\*");

WIN32_FIND_DATAW fd;
HANDLE handle = FindFirstFileW(pathbuf.c_str(), &fd);
if (handle == INVALID_HANDLE_VALUE)
continue;
auto handle_cleanup = defer([handle]() { FindClose(handle); });

entries.clear();
do {
if (fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
continue;
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
const wchar_t *name = fd.cFileName;
if (!wcscmp(name, L".") || !wcscmp(name, L".."))
continue;
pathbuf.assign(dir);
pathbuf.append(name);
pathbuf.push_back(L'\\');
entries.push_back(pathbuf);
}
} while (FindNextFileW(handle, &fd));

std::sort(entries.begin(), entries.end());
for (size_t n = entries.size(); n-- > 0; )
dirs.push_front(std::move(entries[n]));
}
}
#endif

int case_resolve(const char *root_, const char *fragment, std::string &result)
{
if (fragment[0] == '\0')
return 0;

std::string root = path_ensure_final_separator(root_);

std::string pathbuf;
pathbuf.reserve(1024);

pathbuf.assign(root);
pathbuf.append(fragment);
if (exists(pathbuf.c_str())) {
result = std::move(pathbuf);
return 1;
}

struct Item {
std::string root;
string_list components;
};

std::deque<Item> worklist;

{
Item item;
item.root = root;
item.components = split_strings_noempty(fragment, &is_path_separator);
if (item.components.empty())
return 0;
for (size_t i = 0; i + 1 < item.components.size(); ++i)
item.components[i].push_back('/');
if (is_path_separator(fragment[strlen(fragment) - 1]))
item.components.back().push_back('/');
worklist.push_back(std::move(item));
}

while (!worklist.empty()) {
Item item = std::move(worklist.front());
worklist.pop_front();

for (const std::string &entry : list_directory(item.root.c_str())) {
if (ascii_casecmp(entry.c_str(), item.components[0].c_str()) != 0)
continue;

if (item.components.size() == 1) {
pathbuf.assign(item.root);
pathbuf.append(entry);
if (exists(pathbuf.c_str())) {
result = std::move(pathbuf);
return 2;
}
}
else {
assert(item.components.size() > 1);
Item newitem;
newitem.root = item.root + entry;
newitem.components.assign(item.components.begin() + 1, item.components.end());
worklist.push_front(std::move(newitem));
}
}
}

return 0;
}

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

#if defined(_WIN32)
std::wstring widen(const std::string &u8str)
{
return widen(u8str.data(), u8str.size());
}

std::wstring widen(const char *u8data, size_t u8len)
{
if (u8len == ~(size_t)0)
u8len = strlen(u8data);
std::wstring wstr;
int wch = MultiByteToWideChar(CP_UTF8, 0, u8data, (int)u8len, nullptr, 0);
if (wch != 0) {
wstr.resize((size_t)wch);
MultiByteToWideChar(CP_UTF8, 0, u8data, (int)u8len, &wstr[0], wch);
}
return wstr;
}

std::string narrow(const std::wstring &wstr)
{
return narrow(wstr.data(), wstr.size());
}

std::string narrow(const wchar_t *wdata, size_t wlen)
{
if (wlen == ~(size_t)0)
wlen = wcslen(wdata);
std::string u8str;
int u8ch = WideCharToMultiByte(CP_UTF8, 0, wdata, (int)wlen, nullptr, 0, nullptr, nullptr);
if (u8ch != 0) {
u8str.resize((size_t)u8ch);
WideCharToMultiByte(CP_UTF8, 0, wdata, (int)wlen, &u8str[0], u8ch, nullptr, nullptr);
}
return u8str;
}
#endif

} // namespace ysfx

//------------------------------------------------------------------------------
// WDL helpers

// our replacement `atof` for WDL, which is unaffected by current locale
extern "C" double ysfx_wdl_atof(const char *text)
{
return ysfx::dot_atof(text);
}

+ 185
- 0
source/modules/ysfx/sources/ysfx_utils.hpp View File

@@ -0,0 +1,185 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#pragma once
#include "ysfx.h"
#include <string>
#include <vector>
#include <mutex>
#include <cstdio>
#include <cstdint>
#include <clocale>
#if defined(__APPLE__)
# include <xlocale.h>
#endif
#if !defined(_WIN32)
# include <alloca.h>
#else
# include <malloc.h>
#endif

#if defined(YSFX_NO_STANDARD_MUTEX)
# include "WDL/mutex.h"
#endif

namespace ysfx {

YSFX_DEFINE_AUTO_PTR(FILE_u, FILE, fclose);

FILE *fopen_utf8(const char *path, const char *mode);
int64_t fseek_lfs(FILE *stream, int64_t off, int whence);
int64_t ftell_lfs(FILE *stream);

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

#if !defined(_WIN32)
using c_locale_t = locale_t;
#else
using c_locale_t = _locale_t;
#endif

c_locale_t c_numeric_locale();

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

#if !defined(_WIN32)
# define ysfx_alloca(n) alloca((n))
#else
# define ysfx_alloca(n) _malloca((n))
#endif

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

#if !defined(YSFX_NO_STANDARD_MUTEX)
using mutex = std::mutex;
#else
class mutex
{
public:
void lock() { m_mutex.Enter(); }
void unlock() { m_mutex.Leave(); }

private:
WDL_Mutex m_mutex;
};
#endif

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

using string_list = std::vector<std::string>;

double c_atof(const char *text, c_locale_t loc);
double c_strtod(const char *text, char **endp, c_locale_t loc);
double dot_atof(const char *text);
double dot_strtod(const char *text, char **endp);
bool ascii_isspace(char c);
bool ascii_isalpha(char c);
char ascii_tolower(char c);
char ascii_toupper(char c);
int ascii_casecmp(const char *a, const char *b);
uint32_t latin1_toupper(uint32_t c);
uint32_t latin1_tolower(uint32_t c);
char *strdup_using_new(const char *src);
string_list split_strings_noempty(const char *input, bool(*pred)(char));
std::string trim(const char *input, bool(*pred)(char));

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

void pack_u32le(uint32_t value, uint8_t data[4]);
void pack_f32le(float value, uint8_t data[4]);
uint32_t unpack_u32le(const uint8_t data[4]);
float unpack_f32le(const uint8_t data[4]);

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

std::vector<uint8_t> decode_base64(const char *text, size_t len = ~(size_t)0);

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

using file_uid = std::pair<uint64_t, uint64_t>;
bool get_file_uid(const char *path, file_uid &uid);
bool get_stream_file_uid(FILE *stream, file_uid &uid);
bool get_descriptor_file_uid(int fd, file_uid &uid);
#if defined(_WIN32)
bool get_handle_file_uid(void *handle, file_uid &uid);
#endif

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

struct split_path_t {
std::string drive;
std::string dir;
std::string file;
};

// check if the character is a path separator
bool is_path_separator(char ch);
// break down a path into individual components
split_path_t split_path(const char *path);
// get the file name part (all after the final '/' separator)
std::string path_file_name(const char *path);
// get the directory part (all up to the '/' separator, inclusive)
std::string path_directory(const char *path);
// add the final '/' separator if absent; if empty, does nothing
std::string path_ensure_final_separator(const char *path);
// compare the tail of the path with the suffix, case-insensitively
bool path_has_suffix(const char *path, const char *suffix);
// check whether the path is relative
bool path_is_relative(const char *path);

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

// check whether a file exists on disk
bool exists(const char *path);
// list the elements of a directory; directories are distinguished with a final '/'
string_list list_directory(const char *path);
// visit the root and subdirectories in depth-first order
void visit_directories(const char *rootpath, bool (*visit)(const std::string &, void *), void *data);
// resolve a path which matches root/fragment, where fragment is case-insensitive (0=failed, 1=exact, 2=inexact)
int case_resolve(const char *root, const char *fragment, std::string &result);

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

#if defined(_WIN32)
std::wstring widen(const std::string &u8str);
std::wstring widen(const char *u8data, size_t u8len = ~(size_t)0);
std::string narrow(const std::wstring &wstr);
std::string narrow(const wchar_t *wdata, size_t wlen = ~(size_t)0);
#endif

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

template <class F>
class scope_guard {
public:
explicit scope_guard(F &&f) : f(std::forward<F>(f)), a(true) {}
scope_guard(scope_guard &&o) : f(std::move(o.f)), a(o.a) { o.a = false; }
~scope_guard() { if (a) f(); }
void disarm() { a = false; }
private:
F f;
bool a;
scope_guard(const scope_guard &) = delete;
scope_guard &operator=(const scope_guard &) = delete;
};

template <class F> scope_guard<F> defer(F &&f)
{
return scope_guard<F>(std::forward<F>(f));
}

} // namespace ysfx

+ 60
- 0
source/modules/ysfx/sources/ysfx_utils_fts.cpp View File

@@ -0,0 +1,60 @@
// Copyright 2021 Jean Pierre Cimalando
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//

#if !defined(_WIN32)

// fts lacks large file support in glibc < 2.23
#if defined(YSFX_FTS_LACKS_LFS_SUPPORT)
# undef _FILE_OFFSET_BITS
#endif

#include "ysfx_utils.hpp"
#include <fts.h>
#include <string>
#include <cstring>

namespace ysfx {

void visit_directories(const char *rootpath, bool (*visit)(const std::string &, void *), void *data)
{
char *argv[] = {(char *)rootpath, nullptr};

auto compar = [](const FTSENT **a, const FTSENT **b) -> int {
return strcmp((*a)->fts_name, (*b)->fts_name);
};

FTS *fts = fts_open(argv, FTS_NOCHDIR|FTS_PHYSICAL, +compar);
if (!fts)
return;
auto fts_cleanup = defer([fts]() { fts_close(fts); });

std::string pathbuf;
pathbuf.reserve(1024);

while (FTSENT *ent = fts_read(fts)) {
if (ent->fts_info == FTS_D) {
pathbuf.assign(ent->fts_path);
pathbuf.push_back('/');
if (!visit(pathbuf, data))
return;
}
}
}

} // namespace ysfx

#endif // !defined(_WIN32)

+ 19
- 0
source/modules/ysfx/thirdparty/WDL/LICENSE.txt View File

@@ -0,0 +1,19 @@
Copyright (C) 2005 and later Cockos Incorporated

Portions copyright other contributors, see each source file for more information

This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

+ 482
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/assocarray.h View File

@@ -0,0 +1,482 @@
#ifndef _WDL_ASSOCARRAY_H_
#define _WDL_ASSOCARRAY_H_

#include "heapbuf.h"
#include "mergesort.h"

// on all of these, if valdispose is set, the array will dispose of values as needed.
// if keydup/keydispose are set, copies of (any) key data will be made/destroyed as necessary


// WDL_AssocArrayImpl can be used on its own, and can contain structs for keys or values
template <class KEY, class VAL> class WDL_AssocArrayImpl
{
WDL_AssocArrayImpl(const WDL_AssocArrayImpl &cp) { CopyContents(cp); }

WDL_AssocArrayImpl &operator=(const WDL_AssocArrayImpl &cp) { CopyContents(cp); return *this; }

public:

explicit WDL_AssocArrayImpl(int (*keycmp)(KEY *k1, KEY *k2), KEY (*keydup)(KEY)=0, void (*keydispose)(KEY)=0, void (*valdispose)(VAL)=0)
{
m_keycmp = keycmp;
m_keydup = keydup;
m_keydispose = keydispose;
m_valdispose = valdispose;
}

~WDL_AssocArrayImpl()
{
DeleteAll();
}

VAL* GetPtr(KEY key, KEY *keyPtrOut=NULL) const
{
bool ismatch = false;
int i = LowerBound(key, &ismatch);
if (ismatch)
{
KeyVal* kv = m_data.Get()+i;
if (keyPtrOut) *keyPtrOut = kv->key;
return &(kv->val);
}
return 0;
}

bool Exists(KEY key) const
{
bool ismatch = false;
LowerBound(key, &ismatch);
return ismatch;
}

int Insert(KEY key, VAL val)
{
bool ismatch = false;
int i = LowerBound(key, &ismatch);
if (ismatch)
{
KeyVal* kv = m_data.Get()+i;
if (m_valdispose) m_valdispose(kv->val);
kv->val = val;
}
else
{
KeyVal* kv = m_data.Resize(m_data.GetSize()+1)+i;
memmove(kv+1, kv, (m_data.GetSize()-i-1)*(unsigned int)sizeof(KeyVal));
if (m_keydup) key = m_keydup(key);
kv->key = key;
kv->val = val;
}
return i;
}

void Delete(KEY key)
{
bool ismatch = false;
int i = LowerBound(key, &ismatch);
if (ismatch)
{
KeyVal* kv = m_data.Get()+i;
if (m_keydispose) m_keydispose(kv->key);
if (m_valdispose) m_valdispose(kv->val);
m_data.Delete(i);
}
}

void DeleteByIndex(int idx)
{
if (idx >= 0 && idx < m_data.GetSize())
{
KeyVal* kv = m_data.Get()+idx;
if (m_keydispose) m_keydispose(kv->key);
if (m_valdispose) m_valdispose(kv->val);
m_data.Delete(idx);
}
}

void DeleteAll(bool resizedown=false)
{
if (m_keydispose || m_valdispose)
{
int i;
for (i = 0; i < m_data.GetSize(); ++i)
{
KeyVal* kv = m_data.Get()+i;
if (m_keydispose) m_keydispose(kv->key);
if (m_valdispose) m_valdispose(kv->val);
}
}
m_data.Resize(0, resizedown);
}

int GetSize() const
{
return m_data.GetSize();
}

VAL* EnumeratePtr(int i, KEY* key=0) const
{
if (i >= 0 && i < m_data.GetSize())
{
KeyVal* kv = m_data.Get()+i;
if (key) *key = kv->key;
return &(kv->val);
}
return 0;
}
KEY* ReverseLookupPtr(VAL val) const
{
int i;
for (i = 0; i < m_data.GetSize(); ++i)
{
KeyVal* kv = m_data.Get()+i;
if (kv->val == val) return &kv->key;
}
return 0;
}

void ChangeKey(KEY oldkey, KEY newkey)
{
bool ismatch=false;
int i=LowerBound(oldkey, &ismatch);
if (ismatch) ChangeKeyByIndex(i, newkey, true);
}

void ChangeKeyByIndex(int idx, KEY newkey, bool needsort)
{
if (idx >= 0 && idx < m_data.GetSize())
{
KeyVal* kv=m_data.Get()+idx;
if (!needsort)
{
if (m_keydispose) m_keydispose(kv->key);
if (m_keydup) newkey=m_keydup(newkey);
kv->key=newkey;
}
else
{
VAL val=kv->val;
m_data.Delete(idx);
Insert(newkey, val);
}
}
}

// fast add-block mode
void AddUnsorted(KEY key, VAL val)
{
int i=m_data.GetSize();
KeyVal* kv = m_data.Resize(i+1)+i;
if (m_keydup) key = m_keydup(key);
kv->key = key;
kv->val = val;
}

void Resort(int (*new_keycmp)(KEY *k1, KEY *k2)=NULL)
{
if (new_keycmp) m_keycmp = new_keycmp;
if (m_data.GetSize() > 1 && m_keycmp)
{
qsort(m_data.Get(), m_data.GetSize(), sizeof(KeyVal),
(int(*)(const void*, const void*))m_keycmp);
if (!new_keycmp)
RemoveDuplicateKeys();
}
}

void ResortStable()
{
if (m_data.GetSize() > 1 && m_keycmp)
{
char *tmp=(char*)malloc(m_data.GetSize()*sizeof(KeyVal));
if (WDL_NORMALLY(tmp))
{
WDL_mergesort(m_data.Get(), m_data.GetSize(), sizeof(KeyVal),
(int(*)(const void*, const void*))m_keycmp, tmp);
free(tmp);
}
else
{
qsort(m_data.Get(), m_data.GetSize(), sizeof(KeyVal),
(int(*)(const void*, const void*))m_keycmp);
}

RemoveDuplicateKeys();
}
}

int LowerBound(KEY key, bool* ismatch) const
{
int a = 0;
int c = m_data.GetSize();
while (a != c)
{
int b = (a+c)/2;
KeyVal* kv=m_data.Get()+b;
int cmp = m_keycmp(&key, &kv->key);
if (cmp > 0) a = b+1;
else if (cmp < 0) c = b;
else
{
*ismatch = true;
return b;
}
}
*ismatch = false;
return a;
}

int GetIdx(KEY key) const
{
bool ismatch=false;
int i = LowerBound(key, &ismatch);
if (ismatch) return i;
return -1;
}

void SetGranul(int gran)
{
m_data.SetGranul(gran);
}

void CopyContents(const WDL_AssocArrayImpl &cp)
{
m_data=cp.m_data;
m_keycmp = cp.m_keycmp;
m_keydup = cp.m_keydup;
m_keydispose = m_keydup ? cp.m_keydispose : NULL;
m_valdispose = NULL; // avoid disposing of values twice, since we don't have a valdup, we can't have a fully valid copy
if (m_keydup)
{
int x;
const int n=m_data.GetSize();
for (x=0;x<n;x++)
{
KeyVal *kv=m_data.Get()+x;
if (kv->key) kv->key = m_keydup(kv->key);
}
}
}

void CopyContentsAsReference(const WDL_AssocArrayImpl &cp)
{
DeleteAll(true);
m_keycmp = cp.m_keycmp;
m_keydup = NULL; // this no longer can own any data
m_keydispose = NULL;
m_valdispose = NULL;

m_data=cp.m_data;
}


// private data, but exposed in case the caller wants to manipulate at its own risk
struct KeyVal
{
KEY key;
VAL val;
};
WDL_TypedBuf<KeyVal> m_data;

protected:

int (*m_keycmp)(KEY *k1, KEY *k2);
KEY (*m_keydup)(KEY);
void (*m_keydispose)(KEY);
void (*m_valdispose)(VAL);

private:

void RemoveDuplicateKeys() // after resorting
{
const int sz = m_data.GetSize();

int cnt = 1;
KeyVal *rd = m_data.Get() + 1, *wr = rd;
for (int x = 1; x < sz; x ++)
{
if (m_keycmp(&rd->key, &wr[-1].key))
{
if (rd != wr) *wr=*rd;
wr++;
cnt++;
}
else
{
if (m_keydispose) m_keydispose(rd->key);
if (m_valdispose) m_valdispose(rd->val);
}
rd++;
}
if (cnt < sz) m_data.Resize(cnt,false);
}
};


// WDL_AssocArray adds useful functions but cannot contain structs for keys or values
template <class KEY, class VAL> class WDL_AssocArray : public WDL_AssocArrayImpl<KEY, VAL>
{
public:

explicit WDL_AssocArray(int (*keycmp)(KEY *k1, KEY *k2), KEY (*keydup)(KEY)=0, void (*keydispose)(KEY)=0, void (*valdispose)(VAL)=0)
: WDL_AssocArrayImpl<KEY, VAL>(keycmp, keydup, keydispose, valdispose)
{
}

VAL Get(KEY key, VAL notfound=0) const
{
VAL* p = this->GetPtr(key);
if (p) return *p;
return notfound;
}

VAL Enumerate(int i, KEY* key=0, VAL notfound=0) const
{
VAL* p = this->EnumeratePtr(i, key);
if (p) return *p;
return notfound;
}

KEY ReverseLookup(VAL val, KEY notfound=0) const
{
KEY* p=this->ReverseLookupPtr(val);
if (p) return *p;
return notfound;
}
};


template <class VAL> class WDL_IntKeyedArray : public WDL_AssocArray<int, VAL>
{
public:

explicit WDL_IntKeyedArray(void (*valdispose)(VAL)=0) : WDL_AssocArray<int, VAL>(cmpint, NULL, NULL, valdispose) {}
~WDL_IntKeyedArray() {}

private:

static int cmpint(int *i1, int *i2) { return *i1-*i2; }
};

template <class VAL> class WDL_IntKeyedArray2 : public WDL_AssocArrayImpl<int, VAL>
{
public:

explicit WDL_IntKeyedArray2(void (*valdispose)(VAL)=0) : WDL_AssocArrayImpl<int, VAL>(cmpint, NULL, NULL, valdispose) {}
~WDL_IntKeyedArray2() {}

private:

static int cmpint(int *i1, int *i2) { return *i1-*i2; }
};

template <class VAL> class WDL_StringKeyedArray : public WDL_AssocArray<const char *, VAL>
{
public:

explicit WDL_StringKeyedArray(bool caseSensitive=true, void (*valdispose)(VAL)=0) : WDL_AssocArray<const char*, VAL>(caseSensitive?cmpstr:cmpistr, dupstr, freestr, valdispose) {}
~WDL_StringKeyedArray() { }

static const char *dupstr(const char *s) { return strdup(s); } // these might not be necessary but depending on the libc maybe...
static int cmpstr(const char **s1, const char **s2) { return strcmp(*s1, *s2); }
static int cmpistr(const char **a, const char **b) { return stricmp(*a,*b); }
static void freestr(const char* s) { free((void*)s); }
static void freecharptr(char *p) { free(p); }
};


template <class VAL> class WDL_StringKeyedArray2 : public WDL_AssocArrayImpl<const char *, VAL>
{
public:

explicit WDL_StringKeyedArray2(bool caseSensitive=true, void (*valdispose)(VAL)=0) : WDL_AssocArrayImpl<const char*, VAL>(caseSensitive?cmpstr:cmpistr, dupstr, freestr, valdispose) {}
~WDL_StringKeyedArray2() { }

static const char *dupstr(const char *s) { return strdup(s); } // these might not be necessary but depending on the libc maybe...
static int cmpstr(const char **s1, const char **s2) { return strcmp(*s1, *s2); }
static int cmpistr(const char **a, const char **b) { return stricmp(*a,*b); }
static void freestr(const char* s) { free((void*)s); }
static void freecharptr(char *p) { free(p); }
};

// sorts text as text, sorts anything that looks like a number as a number
template <class VAL> class WDL_LogicalSortStringKeyedArray : public WDL_StringKeyedArray<VAL>
{
public:

explicit WDL_LogicalSortStringKeyedArray(bool caseSensitive=true, void (*valdispose)(VAL)=0) : WDL_StringKeyedArray<VAL>(caseSensitive, valdispose)
{
WDL_StringKeyedArray<VAL>::m_keycmp = caseSensitive?cmpstr:cmpistr; // override
}
~WDL_LogicalSortStringKeyedArray() { }

static int cmpstr(const char **a, const char **b) { return _cmpstr(*a, *b, true); }
static int cmpistr(const char **a, const char **b) { return _cmpstr(*a, *b, false); }

private:

static int _cmpstr(const char *s1, const char *s2, bool case_sensitive)
{
// this also exists as WDL_strcmp_logical in wdlcstring.h

for (;;)
{
if (*s1 >= '0' && *s1 <= '9' && *s2 >= '0' && *s2 <= '9')
{
int lzdiff=0, len1=0, len2=0;

while (*s1 == '0') { s1++; lzdiff--; }
while (*s2 == '0') { s2++; lzdiff++; }

while (s1[len1] >= '0' && s1[len1] <= '9') len1++;
while (s2[len2] >= '0' && s2[len2] <= '9') len2++;

if (len1 != len2) return len1-len2;

while (len1--)
{
const int d = *s1++ - *s2++;
if (d) return d;
}

if (lzdiff) return lzdiff;
}
else
{
char c1 = *s1++, c2 = *s2++;
if (c1 != c2)
{
if (case_sensitive) return c1-c2;

if (c1>='a' && c1<='z') c1+='A'-'a';
if (c2>='a' && c2<='z') c2+='A'-'a';
if (c1 != c2) return c1-c2;
}
else if (!c1) return 0;
}
}
}
};


template <class VAL> class WDL_PtrKeyedArray : public WDL_AssocArray<INT_PTR, VAL>
{
public:

explicit WDL_PtrKeyedArray(void (*valdispose)(VAL)=0) : WDL_AssocArray<INT_PTR, VAL>(cmpptr, 0, 0, valdispose) {}

~WDL_PtrKeyedArray() {}

private:
static int cmpptr(INT_PTR* a, INT_PTR* b) { const INT_PTR d = *a - *b; return d<0?-1:(d!=0); }
};


#endif


+ 248
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/denormal.h View File

@@ -0,0 +1,248 @@
#ifndef _WDL_DENORMAL_H_
#define _WDL_DENORMAL_H_

#include <string.h>
#include "wdltypes.h"
// note: the _aggressive versions filter out anything less than around 1.0e-16 or so (approximately) to 0.0, including -0.0 (becomes 0.0)
// note: new! the _aggressive versions also filter inf and NaN to 0.0

#ifdef __cplusplus
#define WDL_DENORMAL_INLINE inline
#elif defined(_MSC_VER)
#define WDL_DENORMAL_INLINE __inline
#else
#ifdef WDL_STATICFUNC_UNUSED
#define WDL_DENORMAL_INLINE WDL_STATICFUNC_UNUSED
#else
#define WDL_DENORMAL_INLINE
#endif
#endif

static WDL_DENORMAL_INLINE unsigned int WDL_DENORMAL_FLOAT_W(const float *a) { unsigned int v; memcpy(&v,a,sizeof(v)); return v; }
static WDL_DENORMAL_INLINE unsigned int WDL_DENORMAL_DOUBLE_HW(const double *a) { WDL_UINT64 v; memcpy(&v,(char*)a,sizeof(v)); return (unsigned int) (v>>32); }

#define WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF 0x3cA00000 // 0x3B8000000 maybe instead? that's 10^-5 smaller or so
#define WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF 0x25000000


// define WDL_DENORMAL_WANTS_SCOPED_FTZ, and then use a WDL_denormal_ftz_scope in addition to denormal_*(), then
// if FTZ is available it will be used instead...
//
#ifdef WDL_DENORMAL_WANTS_SCOPED_FTZ

#if defined(__SSE2__) || _M_IX86_FP >= 2 || defined(_WIN64)
#define WDL_DENORMAL_FTZMODE
#define WDL_DENORMAL_FTZSTATE_TYPE unsigned int
#ifdef _MSC_VER
#include <intrin.h>
#else
#include <xmmintrin.h>
#endif
#define wdl_denorm_mm_getcsr() _mm_getcsr()
#define wdl_denorm_mm_setcsr(x) _mm_setcsr(x)
#if defined(__SSE3__)
#define wdl_denorm_mm_csr_mask ((1<<15)|(1<<11) | (1<<8) | (1<<6)) // FTZ, underflow, denormal mask, DAZ
#else
#define wdl_denorm_mm_csr_mask ((1<<15)|(1<<11)) // FTZ and underflow only (target SSE2)
#endif
#elif defined(__arm__) || defined(__aarch64__)
#define WDL_DENORMAL_FTZMODE
#define WDL_DENORMAL_FTZSTATE_TYPE unsigned long
static unsigned long __attribute__((unused)) wdl_denorm_mm_getcsr()
{
unsigned long rv;
#ifdef __aarch64__
asm volatile ( "mrs %0, fpcr" : "=r" (rv));
#else
asm volatile ( "fmrx %0, fpscr" : "=r" (rv));
#endif
return rv;
}
static void __attribute__((unused)) wdl_denorm_mm_setcsr(unsigned long v)
{
#ifdef __aarch64__
asm volatile ( "msr fpcr, %0" :: "r"(v));
#else
asm volatile ( "fmxr fpscr, %0" :: "r"(v));
#endif
}
#define wdl_denorm_mm_csr_mask (1<<24)
#endif

class WDL_denormal_ftz_scope
{
public:
WDL_denormal_ftz_scope()
{
#ifdef WDL_DENORMAL_FTZMODE
const WDL_DENORMAL_FTZSTATE_TYPE b = wdl_denorm_mm_csr_mask;
old_state = wdl_denorm_mm_getcsr();
if ((need_restore = (old_state & b) != b))
wdl_denorm_mm_setcsr(old_state|b);
#endif
}
~WDL_denormal_ftz_scope()
{
#ifdef WDL_DENORMAL_FTZMODE
if (need_restore) wdl_denorm_mm_setcsr(old_state);
#endif
}

#ifdef WDL_DENORMAL_FTZMODE
WDL_DENORMAL_FTZSTATE_TYPE old_state;
bool need_restore;
#endif

};


#endif


#if !defined(WDL_DENORMAL_FTZMODE) && !defined(WDL_DENORMAL_DO_NOT_FILTER)

static double WDL_DENORMAL_INLINE denormal_filter_double(double a)
{
return (WDL_DENORMAL_DOUBLE_HW(&a)&0x7ff00000) ? a : 0.0;
}

static double WDL_DENORMAL_INLINE denormal_filter_double2(double a)
{
return ((WDL_DENORMAL_DOUBLE_HW(&a)+0x100000)&0x7ff00000) > 0x100000 ? a : 0.0;
}

static double WDL_DENORMAL_INLINE denormal_filter_double_aggressive(double a)
{
return ((WDL_DENORMAL_DOUBLE_HW(&a)+0x100000)&0x7ff00000) >= WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF ? a : 0.0;
}

static float WDL_DENORMAL_INLINE denormal_filter_float(float a)
{
return (WDL_DENORMAL_FLOAT_W(&a)&0x7f800000) ? a : 0.0f;
}

static float WDL_DENORMAL_INLINE denormal_filter_float2(float a)
{
return ((WDL_DENORMAL_FLOAT_W(&a)+0x800000)&0x7f800000) > 0x800000 ? a : 0.0f;
}


static float WDL_DENORMAL_INLINE denormal_filter_float_aggressive(float a)
{
return ((WDL_DENORMAL_FLOAT_W(&a)+0x800000)&0x7f800000) >= WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF ? a : 0.0f;
}
static void WDL_DENORMAL_INLINE denormal_fix_double(double *a)
{
if (!(WDL_DENORMAL_DOUBLE_HW(a)&0x7ff00000)) *a=0.0;
}

static void WDL_DENORMAL_INLINE denormal_fix_double_aggressive(double *a)
{
if (((WDL_DENORMAL_DOUBLE_HW(a)+0x100000)&0x7ff00000) < WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF) *a=0.0;
}

static void WDL_DENORMAL_INLINE denormal_fix_float(float *a)
{
if (!(WDL_DENORMAL_FLOAT_W(a)&0x7f800000)) *a=0.0f;
}
static void WDL_DENORMAL_INLINE denormal_fix_float_aggressive(float *a)
{
if (((WDL_DENORMAL_FLOAT_W(a)+0x800000)&0x7f800000) < WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF) *a=0.0f;
}



#ifdef __cplusplus // automatic typed versions (though one should probably use the explicit versions...


static double WDL_DENORMAL_INLINE denormal_filter(double a)
{
return (WDL_DENORMAL_DOUBLE_HW(&a)&0x7ff00000) ? a : 0.0;
}
static double WDL_DENORMAL_INLINE denormal_filter_aggressive(double a)
{
return ((WDL_DENORMAL_DOUBLE_HW(&a)+0x100000)&0x7ff00000) >= WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF ? a : 0.0;
}

static float WDL_DENORMAL_INLINE denormal_filter(float a)
{
return (WDL_DENORMAL_FLOAT_W(&a)&0x7f800000) ? a : 0.0f;
}

static float WDL_DENORMAL_INLINE denormal_filter_aggressive(float a)
{
return ((WDL_DENORMAL_FLOAT_W(&a)+0x800000)&0x7f800000) >= WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF ? a : 0.0f;
}

static void WDL_DENORMAL_INLINE denormal_fix(double *a)
{
if (!(WDL_DENORMAL_DOUBLE_HW(a)&0x7ff00000)) *a=0.0;
}
static void WDL_DENORMAL_INLINE denormal_fix_aggressive(double *a)
{
if (((WDL_DENORMAL_DOUBLE_HW(a)+0x100000)&0x7ff00000) < WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF) *a=0.0;
}
static void WDL_DENORMAL_INLINE denormal_fix(float *a)
{
if (!(WDL_DENORMAL_FLOAT_W(a)&0x7f800000)) *a=0.0f;
}
static void WDL_DENORMAL_INLINE denormal_fix_aggressive(float *a)
{
if (((WDL_DENORMAL_FLOAT_W(a)+0x800000)&0x7f800000) < WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF) *a=0.0f;
}



#endif // cplusplus versions

#else // end of !WDL_DENORMAL_DO_NOT_FILTER (and other platform-specific checks)

#define denormal_filter(x) (x)
#define denormal_filter2(x) (x)
#define denormal_filter_double(x) (x)
#define denormal_filter_double2(x) (x)
#define denormal_filter_double_aggressive(x) (x)
#define denormal_filter_float(x) (x)
#define denormal_filter_float2(x) (x)
#define denormal_filter_float_aggressive(x) (x)
#define denormal_filter_aggressive(x) (x)
#define denormal_fix(x) do { } while(0)
#define denormal_fix_aggressive(x) do { } while(0)
#define denormal_fix_double(x) do { } while(0)
#define denormal_fix_double_aggressive(x) do { } while(0)
#define denormal_fix_float(x) do { } while(0)
#define denormal_fix_float_aggressive(x) do { } while(0)

#endif


////////////////////
// this isnt a denormal function but it is similar, so we'll put it here as a bonus

static void WDL_DENORMAL_INLINE GetDoubleMaxAbsValue(double *out, const double *in) // note: the value pointed to by "out" must be >=0.0, __NOT__ <= -0.0
{
WDL_UINT64 i, o;
memcpy(&i,in,sizeof(i));
memcpy(&o,out,sizeof(o));
i &= WDL_UINT64_CONST(0x7fffffffffffffff);
if (i > o) memcpy(out,&i,sizeof(i));
}

static void WDL_DENORMAL_INLINE GetFloatMaxAbsValue(float *out, const float *in) // note: the value pointed to by "out" must be >=0.0, __NOT__ <= -0.0
{
unsigned int i, o;
memcpy(&i, in, sizeof(i));
memcpy(&o, out, sizeof(o));
i &= 0x7fffffff;
if (i > o) memcpy(out, &i, sizeof(i));
}


#ifdef __cplusplus
static void WDL_DENORMAL_INLINE GetFloatMaxAbsValue(double *out, const double *in) // note: the value pointed to by "out" must be >=0.0, __NOT__ <= -0.0
{
GetDoubleMaxAbsValue(out,in);
}
#endif

#endif

+ 329
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/dirscan.h View File

@@ -0,0 +1,329 @@
/*
WDL - dirscan.h
Copyright (C) 2005 and later Cockos Incorporated
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/

/*

This file provides the interface and implementation for WDL_DirScan, a simple
(and somewhat portable) directory reading class. On non-Win32 systems it wraps
opendir()/readdir()/etc. On Win32, it uses FindFirst*, and supports wildcards as
well.

*/


#ifndef _WDL_DIRSCAN_H_
#define _WDL_DIRSCAN_H_

#include "wdlstring.h"

#ifndef _WIN32
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
extern struct stat wdl_stat_chk;
// if this fails on linux, use CFLAGS += -D_FILE_OFFSET_BITS=64
typedef char wdl_dirscan_assert_failed_stat_not_64[sizeof(wdl_stat_chk.st_size)!=8 ? -1 : 1];
#endif

class WDL_DirScan
{
public:
WDL_DirScan() :
#ifdef _WIN32
m_h(INVALID_HANDLE_VALUE)
#ifndef WDL_NO_SUPPORT_UTF8
, m_wcmode(false)
#endif
#else
m_h(NULL), m_ent(NULL)
#endif
{
}

~WDL_DirScan()
{
Close();
}

int First(const char *dirname
#ifdef _WIN32
, int isExactSpec=0
#endif
) // returns 0 if success
{
WDL_FastString scanstr(dirname);
const int l = scanstr.GetLength();
if (l < 1) return -1;

#ifdef _WIN32
if (!isExactSpec)
{
if (dirname[l-1] == '\\' || dirname[l-1] == '/') scanstr.SetLen(l-1);
m_leading_path = scanstr;
scanstr.Append("\\*");
}
else
{
m_leading_path = scanstr;

// remove trailing wildcards and directory separator from m_leading_path
const char *sp = m_leading_path.Get();
int idx = m_leading_path.GetLength() - 1;
while (idx > 0 && sp[idx] != '/' && sp[idx] != '\\') idx--;
if (idx > 0) m_leading_path.SetLen(idx);
}
#else
if (dirname[l-1] == '\\' || dirname[l-1] == '/') scanstr.SetLen(l-1);
m_leading_path = scanstr;
if (!scanstr.GetLength()) scanstr.Set("/"); // fix for scanning /
#endif

Close();
#ifdef _WIN32
#ifndef WDL_NO_SUPPORT_UTF8
m_h=INVALID_HANDLE_VALUE;
#ifdef WDL_SUPPORT_WIN9X
m_wcmode = GetVersion()< 0x80000000;
#else
m_wcmode = true;
#endif

if (m_wcmode)
{
int reqbuf = MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,scanstr.Get(),-1,NULL,0);
if (reqbuf > 1000)
{
WDL_TypedBuf<WCHAR> tmp;
tmp.Resize(reqbuf+20);
if (MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,scanstr.Get(),-1,tmp.Get(),tmp.GetSize()-10))
{
correctlongpath(tmp.Get());
m_h=FindFirstFileW(tmp.Get(),&m_fd);
}
}
else
{
WCHAR wfilename[1024];
if (MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,scanstr.Get(),-1,wfilename,1024-10))
{
correctlongpath(wfilename);
m_h=FindFirstFileW(wfilename,&m_fd);
}
}
}
if (m_h==INVALID_HANDLE_VALUE) m_wcmode=false;
if (m_h==INVALID_HANDLE_VALUE)
#endif
m_h=FindFirstFile(scanstr.Get(),(WIN32_FIND_DATA*)&m_fd);
return (m_h == INVALID_HANDLE_VALUE);
#else
m_ent=0;
m_h=opendir(scanstr.Get());
return !m_h || Next();
#endif
}
int Next() // returns 0 on success
{
#ifdef _WIN32
if (m_h == INVALID_HANDLE_VALUE) return -1;
#ifndef WDL_NO_SUPPORT_UTF8
if (m_wcmode) return !FindNextFileW(m_h,&m_fd);
#endif
return !FindNextFile(m_h,(WIN32_FIND_DATA*)&m_fd);
#else
if (!m_h) return -1;
return !(m_ent=readdir(m_h));
#endif
}
void Close()
{
#ifdef _WIN32
if (m_h != INVALID_HANDLE_VALUE) FindClose(m_h);
m_h=INVALID_HANDLE_VALUE;
#else
if (m_h) closedir(m_h);
m_h=0; m_ent=0;
#endif
}

#ifdef _WIN32
const char *GetCurrentFN()
{
#ifndef WDL_NO_SUPPORT_UTF8
if (m_wcmode)
{
if (!WideCharToMultiByte(CP_UTF8,0,m_fd.cFileName,-1,m_tmpbuf,sizeof(m_tmpbuf),NULL,NULL))
m_tmpbuf[0]=0;
return m_tmpbuf;
}
#endif
return ((WIN32_FIND_DATA *)&m_fd)->cFileName;
}
#else
const char *GetCurrentFN() const { return m_ent?m_ent->d_name : ""; }
#endif
template<class T> void GetCurrentFullFN(T *str)
{
str->Set(m_leading_path.Get());
#ifdef _WIN32
str->Append("\\");
#else
str->Append("/");
#endif
str->Append(GetCurrentFN());
}
int GetCurrentIsDirectory() const // returns 1 if dir, 2 if symlink to dir, 4 if possibly-recursive symlink to dir
{
#ifdef _WIN32
return !!(m_fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
#else
char tmp[2048];
if (m_ent) switch (m_ent->d_type)
{
case DT_DIR: return 1;
case DT_LNK:
{
snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),m_ent->d_name);
char *rp = realpath(tmp,NULL);
if (!rp) return 0;

struct stat sb;
int ret = (!stat(rp,&sb) && (sb.st_mode & S_IFMT) == S_IFDIR) ? 2 : 0;
if (ret)
{
// treat symlinks of /path/to/foo -> /path from being resolved (avoiding obvious feedback loops)
const int rpl = (int) strlen(rp);
if (
#ifdef __APPLE__
!strnicmp(rp,m_leading_path.Get(),rpl)
#else
!strncmp(rp,m_leading_path.Get(),rpl)
#endif
&& (m_leading_path.Get()[rpl] == '/' || m_leading_path.Get()[rpl] == 0)
) ret = 4;
}
free(rp);
return ret;
}
case DT_UNKNOWN:
{
snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),m_ent->d_name);
DIR *d = opendir(tmp);
if (d) { closedir(d); return 1; }
return 0;
}
}
return 0;
#endif
}

// these are somewhat windows specific calls, eh
#ifdef _WIN32
DWORD GetCurrentFileSize(DWORD *HighWord=NULL) const { if (HighWord) *HighWord = m_fd.nFileSizeHigh; return m_fd.nFileSizeLow; }
void GetCurrentLastWriteTime(FILETIME *ft) const { *ft = m_fd.ftLastWriteTime; }
void GetCurrentLastAccessTime(FILETIME *ft) const { *ft = m_fd.ftLastAccessTime; }
void GetCurrentCreationTime(FILETIME *ft) const { *ft = m_fd.ftCreationTime; }
DWORD GetFileAttributes() const { return m_fd.dwFileAttributes; }
#elif defined(_WDL_SWELL_H_)

void GetCurrentCreationTime(FILETIME *ft)
{
char tmp[2048];
snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),GetCurrentFN());
struct stat st={0,};
stat(tmp,&st);
unsigned long long a=(unsigned long long)st.st_ctime; // seconds since january 1st, 1970
a+=11644473600ull; // 1601->1970
a*=10000000; // seconds to 1/10th microseconds (100 nanoseconds)
ft->dwLowDateTime=a & 0xffffffff;
ft->dwHighDateTime=a>>32;
}

void GetCurrentLastWriteTime(FILETIME *ft)
{
char tmp[2048];
snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),GetCurrentFN());
struct stat st={0,};
stat(tmp,&st);
unsigned long long a=(unsigned long long)st.st_mtime; // seconds since january 1st, 1970
a+=11644473600ull; // 1601->1970
a*=10000000; // seconds to 1/10th microseconds (100 nanoseconds)
ft->dwLowDateTime=a & 0xffffffff;
ft->dwHighDateTime=a>>32;
}
DWORD GetCurrentFileSize(DWORD *HighWord=NULL)
{
char tmp[2048];
snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),GetCurrentFN());
struct stat st={0,};
stat(tmp,&st);
if (HighWord) *HighWord = (DWORD)(st.st_size>>32);
return (DWORD)(st.st_size&0xffffffff);
}
#endif

private:
#ifdef _WIN32

#ifndef WDL_NO_SUPPORT_UTF8
bool m_wcmode;
WIN32_FIND_DATAW m_fd;
char m_tmpbuf[MAX_PATH*5]; // even if each byte gets encoded as 4 utf-8 bytes this should be plenty ;)
#else
WIN32_FIND_DATA m_fd;
#endif
HANDLE m_h;
#else
DIR *m_h;
struct dirent *m_ent;
#endif
WDL_FastString m_leading_path;

#ifdef _WIN32
static void correctlongpath(WCHAR *buf) // this also exists as wdl_utf8_correctlongpath
{
const WCHAR *insert;
WCHAR *wr;
int skip = 0;
if (!buf || !buf[0] || wcslen(buf) < 256) return;
if (buf[1] == ':') insert=L"\\\\?\\";
else if (buf[0] == '\\' && buf[1] == '\\') { insert = L"\\\\?\\UNC\\"; skip=2; }
else return;

wr = buf + wcslen(insert);
memmove(wr, buf + skip, (wcslen(buf+skip)+1)*2);
memmove(buf,insert,wcslen(insert)*2);
while (*wr)
{
if (*wr == '/') *wr = '\\';
wr++;
}
}
#endif
} WDL_FIXALIGN;

#endif

+ 1270
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-aarch64-gcc.c
File diff suppressed because it is too large
View File


+ 1308
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-arm-gcc.c
File diff suppressed because it is too large
View File


+ 1436
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-ppc-gcc.c
File diff suppressed because it is too large
View File


+ 1357
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-x64-sse.asm
File diff suppressed because it is too large
View File


+ 1332
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-x86-gcc.c
File diff suppressed because it is too large
View File


+ 3017
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-x86-msvc.c
File diff suppressed because it is too large
View File


+ 113
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel2.l View File

@@ -0,0 +1,113 @@
%option reentrant
%option prefix="nseel"
%option bison-bridge
%option bison-locations
%option noyywrap
%option never-interactive
%option batch
%option nounput
%{
#include <stdlib.h>
#include <stdio.h>
#define YY_USER_ACTION yylloc->first_line = yylineno;
#define YY_FATAL_ERROR(msg) { ((struct yyguts_t*)yyscanner)->yyextra_r->errVar=1; }
#define YY_INPUT(buf,result,max_size) { (result) = nseel_gets(yyextra,(buf),max_size); }
#define YY_EXTRA_TYPE compileContext *
#undef YY_BUF_SIZE
#define YY_BUF_SIZE (NSEEL_MAX_VARIABLE_NAMELEN*2)
#undef YY_READ_BUF_SIZE
#define YY_READ_BUF_SIZE (NSEEL_MAX_VARIABLE_NAMELEN)
#include "y.tab.h"
#ifdef _WIN32
#define YY_NO_UNISTD_H
#endif
#include "ns-eel-int.h"
int nseel_gets(compileContext *ctx, char *buf, size_t sz);
#define PARSENUM *yylval = nseel_translate(yyextra,yytext, 0); return VALUE;
#define EEL_ACTION(x) return x;
#ifdef stdin
#undef stdin
#endif
#define stdin (0)
#ifdef stdout
#undef stdout
#endif
#define stdout (0)
static int g_fake_errno;
#ifdef errno
#undef errno
#endif
#define errno g_fake_errno
static void comment(yyscan_t yyscanner);
%}
%%
[0-9]+\.?[0-9]* PARSENUM;
\.[0-9]+ PARSENUM;
0[xX][0-9a-fA-F]* PARSENUM;
\$[xX][0-9a-fA-F]* PARSENUM;
\$\~[0-9]* PARSENUM;
\$[Ee] PARSENUM;
\$[Pp][Ii] PARSENUM;
\$[Pp][Hh][Ii] PARSENUM;
\$\'.\' PARSENUM;
\#[a-zA-Z0-9\._]* *yylval = nseel_translate(yyextra,yytext, 0); return STRING_IDENTIFIER;
\<\< return TOKEN_SHL;
\>\> return TOKEN_SHR;
\<= return TOKEN_LTE;
\>= return TOKEN_GTE;
== return TOKEN_EQ;
=== return TOKEN_EQ_EXACT;
\!= return TOKEN_NE;
\!== return TOKEN_NE_EXACT;
\&\& return TOKEN_LOGICAL_AND;
\|\| return TOKEN_LOGICAL_OR;
\+= return TOKEN_ADD_OP;
-= return TOKEN_SUB_OP;
%= return TOKEN_MOD_OP;
\|= return TOKEN_OR_OP;
\&= return TOKEN_AND_OP;
\~= return TOKEN_XOR_OP;
\/= return TOKEN_DIV_OP;
\*= return TOKEN_MUL_OP;
\^= return TOKEN_POW_OP;
[a-zA-Z_][a-zA-Z0-9\._]* &yylval = nseel_createCompiledValuePtr((compileContext *)yyextra, NULL, yytext); return IDENTIFIER;
[ \t\r\n]+ /* whitespace */
\/\/.*$ /* comment */
"/*" { comment(yyscanner); }
. return (int)yytext[0];
%%
static void comment(yyscan_t yyscanner)
{
int c,lc=0;
while (0 != (c = input(yyscanner)))
{
if (c == '/' && lc == '*') return;
lc = c;
}
// end of file, ignore for now
}

+ 376
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel2.y View File

@@ -0,0 +1,376 @@
%pure-parser
%name-prefix="nseel"
%parse-param { compileContext* context }
%lex-param { void* scanner }


/* this will prevent y.tab.c from ever calling yydestruct(), since we do not use it and it is a waste */
%destructor {
#define yydestruct(a,b,c,d,e)
} VALUE


%{
#ifdef _WIN32
#include <windows.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include "y.tab.h"
#include "ns-eel-int.h"
#define scanner context->scanner
#define YY_(x) ("")

%}

%token VALUE IDENTIFIER TOKEN_SHL TOKEN_SHR
%token TOKEN_LTE TOKEN_GTE TOKEN_EQ TOKEN_EQ_EXACT TOKEN_NE TOKEN_NE_EXACT TOKEN_LOGICAL_AND TOKEN_LOGICAL_OR
%token TOKEN_ADD_OP TOKEN_SUB_OP TOKEN_MOD_OP TOKEN_OR_OP TOKEN_AND_OP TOKEN_XOR_OP TOKEN_DIV_OP TOKEN_MUL_OP TOKEN_POW_OP
%token STRING_LITERAL STRING_IDENTIFIER
%expect 75
%start program

%%

more_params:
expression
| expression ',' more_params
{
$$ = nseel_createMoreParametersOpcode(context,$1,$3);
}
;

string:
STRING_LITERAL
| STRING_LITERAL string
{
((struct eelStringSegmentRec *)$1)->_next = (struct eelStringSegmentRec *)$2;
$$ = $1;
}
;

assignable_value:
IDENTIFIER
{
if (!($$ = nseel_resolve_named_symbol(context, $1, -1, NULL))) /* convert from purely named to namespace-relative, etc */
{
yyerror(&yyloc, context, "");
YYERROR;
}
}
/* we used to have VALUE in here rather than rvalue, to allow 1=1 1+=2 etc, but silly to,
though this breaks Vmorph, which does 1=1 for a nop, and Jonas DrumReaplacer, which does x = 0 = y = 0 */
| '(' expression ')'
{
$$ = $2;
}
| IDENTIFIER '(' expression ')' '(' expression ')'
{
int err;
if (!($$ = nseel_setCompiledFunctionCallParameters(context,$1, $3, 0, 0, $6, &err)))
{
if (err == -1) yyerror(&yylsp[-2], context, "");
else if (err == 0) yyerror(&yylsp[-6], context, "");
else yyerror(&yylsp[-3], context, ""); // parameter count wrong

YYERROR;
}
}
| IDENTIFIER '(' expression ')'
{
int err;
if (!($$ = nseel_setCompiledFunctionCallParameters(context,$1, $3, 0, 0, 0, &err)))
{
if (err == 0) yyerror(&yylsp[-3], context, "");
else yyerror(&yylsp[0], context, ""); // parameter count wrong
YYERROR;
}
}
| IDENTIFIER '(' ')'
{
int err;
if (!($$ = nseel_setCompiledFunctionCallParameters(context,$1, nseel_createCompiledValue(context,0.0), 0, 0, 0,&err)))
{
if (err == 0) yyerror(&yylsp[-2], context, ""); // function not found
else yyerror(&yylsp[0], context, ""); // parameter count wrong
YYERROR;
}
}
| IDENTIFIER '(' expression ',' expression ')'
{
int err;
if (!($$ = nseel_setCompiledFunctionCallParameters(context,$1, $3, $5, 0, 0,&err)))
{
if (err == 0) yyerror(&yylsp[-5], context, "");
else if (err == 2) yyerror(&yylsp[0], context, ""); // needs more than 2 parameters
else yyerror(&yylsp[-2], context, ""); // less than 2
YYERROR;
}
}
| IDENTIFIER '(' expression ',' expression ',' more_params ')'
{
int err;
if (!($$ = nseel_setCompiledFunctionCallParameters(context,$1, $3, $5, $7, 0, &err)))
{
if (err == 0) yyerror(&yylsp[-7], context, "");
else if (err==2) yyerror(&yylsp[0], context, ""); // needs more parameters
else if (err==4) yyerror(&yylsp[-4], context, ""); // needs single parameter
else yyerror(&yylsp[-2], context, ""); // less parm
YYERROR;
}
}
| rvalue '[' ']'
{
$$ = nseel_createMemoryAccess(context,$1,0);
}
| rvalue '[' expression ']'
{
$$ = nseel_createMemoryAccess(context,$1,$3);
}
;

rvalue:
VALUE
| STRING_IDENTIFIER
| string
{
$$ = nseel_eelMakeOpcodeFromStringSegments(context,(struct eelStringSegmentRec *)$1);
}
| assignable_value
;


assignment:
rvalue
| assignable_value '=' if_else_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_ASSIGN,2,$1,$3);
}
| assignable_value TOKEN_ADD_OP if_else_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_ADD_OP,2,$1,$3);
}
| assignable_value TOKEN_SUB_OP if_else_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_SUB_OP,2,$1,$3);
}
| assignable_value TOKEN_MOD_OP if_else_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_MOD_OP,2,$1,$3);
}
| assignable_value TOKEN_OR_OP if_else_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_OR_OP,2,$1,$3);
}
| assignable_value TOKEN_AND_OP if_else_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_AND_OP,2,$1,$3);
}
| assignable_value TOKEN_XOR_OP if_else_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_XOR_OP,2,$1,$3);
}
| assignable_value TOKEN_DIV_OP if_else_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_DIV_OP,2,$1,$3);
}
| assignable_value TOKEN_MUL_OP if_else_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_MUL_OP,2,$1,$3);
}
| assignable_value TOKEN_POW_OP if_else_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_POW_OP,2,$1,$3);
}
| STRING_IDENTIFIER '=' if_else_expr
{
$$ = nseel_createFunctionByName(context,"strcpy",2,$1,$3,NULL);
}
| STRING_IDENTIFIER TOKEN_ADD_OP if_else_expr
{
$$ = nseel_createFunctionByName(context,"strcat",2,$1,$3,NULL);
}
;

unary_expr:
assignment
| '+' unary_expr
{
$$ = $2;
}
| '-' unary_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_UMINUS,1,$2,0);
}
| '!' unary_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_NOT,1,$2,0);
}
;

pow_expr:
unary_expr
| pow_expr '^' unary_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_POW,2,$1,$3);
}
;

mod_expr:
pow_expr
| mod_expr '%' pow_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_MOD,2,$1,$3);
}
| mod_expr TOKEN_SHL pow_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_SHL,2,$1,$3);
}
| mod_expr TOKEN_SHR pow_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_SHR,2,$1,$3);
}
;

div_expr:
mod_expr
| div_expr '/' mod_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_DIVIDE,2,$1,$3);
}
;


mul_expr:
div_expr
| mul_expr '*' div_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_MULTIPLY,2,$1,$3);
}
;


sub_expr:
mul_expr
| sub_expr '-' mul_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_SUB,2,$1,$3);
}
;

add_expr:
sub_expr
| add_expr '+' sub_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_ADD,2,$1,$3);
}
;

andor_expr:
add_expr
| andor_expr '&' add_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_AND,2,$1,$3);
}
| andor_expr '|' add_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_OR,2,$1,$3);
}
| andor_expr '~' add_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_XOR,2,$1,$3);
}
;

cmp_expr:
andor_expr
| cmp_expr '<' andor_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_LT,2,$1,$3);
}
| cmp_expr '>' andor_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_GT,2,$1,$3);
}
| cmp_expr TOKEN_LTE andor_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_LTE,2,$1,$3);
}
| cmp_expr TOKEN_GTE andor_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_GTE,2,$1,$3);
}
| cmp_expr TOKEN_EQ andor_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_EQ,2,$1,$3);
}
| cmp_expr TOKEN_EQ_EXACT andor_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_EQ_EXACT,2,$1,$3);
}
| cmp_expr TOKEN_NE andor_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_NE,2,$1,$3);
}
| cmp_expr TOKEN_NE_EXACT andor_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_NE_EXACT,2,$1,$3);
}
;

logical_and_or_expr:
cmp_expr
| logical_and_or_expr TOKEN_LOGICAL_AND cmp_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_LOGICAL_AND,2,$1,$3);
}
| logical_and_or_expr TOKEN_LOGICAL_OR cmp_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_LOGICAL_OR,2,$1,$3);
}
;

if_else_expr:
logical_and_or_expr
| logical_and_or_expr '?' if_else_expr ':' if_else_expr
{
$$ = nseel_createIfElse(context, $1, $3, $5);
}
| logical_and_or_expr '?' ':' if_else_expr
{
$$ = nseel_createIfElse(context, $1, 0, $4);
}
| logical_and_or_expr '?' if_else_expr
{
$$ = nseel_createIfElse(context, $1, $3, 0);
}
;


expression:
if_else_expr
| expression ';' if_else_expr
{
$$ = nseel_createSimpleCompiledFunction(context,FN_JOIN_STATEMENTS,2,$1,$3);
}
| expression ';'
{
$$ = $1;
}
;


program:
expression
{
if (@1.first_line) { }
context->result = $1;
}
;


%%

+ 71
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_atomic.h View File

@@ -0,0 +1,71 @@
#ifndef __EEL_ATOMIC_H__
#define __EEL_ATOMIC_H__

// requires these to be defined
//#define EEL_ATOMIC_SET_SCOPE(opaque) WDL_Mutex *mutex = (opaque?&((effectProcessor *)opaque)->m_atomic_mutex:&atomic_mutex);
//#define EEL_ATOMIC_ENTER mutex->Enter()
//#define EEL_ATOMIC_LEAVE mutex->Leave()

static EEL_F NSEEL_CGEN_CALL atomic_setifeq(void *opaque, EEL_F *a, EEL_F *cmp, EEL_F *nd)
{
EEL_F ret;
EEL_ATOMIC_SET_SCOPE(opaque)
EEL_ATOMIC_ENTER;
ret = *a;
if (fabs(ret - *cmp) < NSEEL_CLOSEFACTOR) *a = *nd;
EEL_ATOMIC_LEAVE;
return ret;
}

static EEL_F NSEEL_CGEN_CALL atomic_exch(void *opaque, EEL_F *a, EEL_F *b)
{
EEL_F tmp;
EEL_ATOMIC_SET_SCOPE(opaque)
EEL_ATOMIC_ENTER;
tmp = *b;
*b = *a;
*a = tmp;
EEL_ATOMIC_LEAVE;
return tmp;
}

static EEL_F NSEEL_CGEN_CALL atomic_add(void *opaque, EEL_F *a, EEL_F *b)
{
EEL_F tmp;
EEL_ATOMIC_SET_SCOPE(opaque)
EEL_ATOMIC_ENTER;
tmp = (*a += *b);
EEL_ATOMIC_LEAVE;
return tmp;
}

static EEL_F NSEEL_CGEN_CALL atomic_set(void *opaque, EEL_F *a, EEL_F *b)
{
EEL_F tmp;
EEL_ATOMIC_SET_SCOPE(opaque)
EEL_ATOMIC_ENTER;
tmp = *a = *b;
EEL_ATOMIC_LEAVE;
return tmp;
}

static EEL_F NSEEL_CGEN_CALL atomic_get(void *opaque, EEL_F *a)
{
EEL_F tmp;
EEL_ATOMIC_SET_SCOPE(opaque)
EEL_ATOMIC_ENTER;
tmp = *a;
EEL_ATOMIC_LEAVE;
return tmp;
}

static void EEL_atomic_register()
{
NSEEL_addfunc_retval("atomic_setifequal",3, NSEEL_PProc_THIS, &atomic_setifeq);
NSEEL_addfunc_retval("atomic_exch",2, NSEEL_PProc_THIS, &atomic_exch);
NSEEL_addfunc_retval("atomic_add",2, NSEEL_PProc_THIS, &atomic_add);
NSEEL_addfunc_retval("atomic_set",2, NSEEL_PProc_THIS, &atomic_set);
NSEEL_addfunc_retval("atomic_get",1, NSEEL_PProc_THIS, &atomic_get);
}

#endif

+ 81
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_eval.h View File

@@ -0,0 +1,81 @@
#ifndef _EEL_EVAL_H_
#define _EEL_EVAL_H_

#ifndef EEL_EVAL_GET_CACHED
#define EEL_EVAL_GET_CACHED(str, ch) (NULL)
#endif

#ifndef EEL_EVAL_SET_CACHED
#define EEL_EVAL_SET_CACHED(sv, ch) { NSEEL_code_free(ch); free(sv); }
#endif

#ifndef EEL_EVAL_SCOPE_ENTER
#define EEL_EVAL_SCOPE_ENTER 1
#define EEL_EVAL_SCOPE_LEAVE
#endif

static EEL_F NSEEL_CGEN_CALL _eel_eval(void *opaque, EEL_F *s)
{
NSEEL_VMCTX r = EEL_EVAL_GET_VMCTX(opaque);
NSEEL_CODEHANDLE ch = NULL;
char *sv=NULL;
if (r)
{
EEL_STRING_MUTEXLOCK_SCOPE
const char *str=EEL_STRING_GET_FOR_INDEX(*s,NULL);
#ifdef EEL_STRING_DEBUGOUT
if (!str)
{
EEL_STRING_DEBUGOUT("eval() passed invalid string handle %f",*s);
}
#endif
if (str && *str)
{
sv=EEL_EVAL_GET_CACHED(str,ch);
if (!sv) sv=strdup(str);
}
}
if (sv)
{
if (!ch) ch = NSEEL_code_compile(r,sv,0);
if (ch)
{
if (EEL_EVAL_SCOPE_ENTER)
{
NSEEL_code_execute(ch);
EEL_EVAL_SCOPE_LEAVE
}
else
{
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("eval() reentrancy limit reached");
#endif
}

EEL_EVAL_SET_CACHED(sv,ch);
return 1.0;
}
else
{
#ifdef EEL_STRING_DEBUGOUT
const char *err=NSEEL_code_getcodeerror(r);
if (err) EEL_STRING_DEBUGOUT("eval() error: %s",err);
#endif
}
free(sv);
}
return 0.0;
}

void EEL_eval_register()
{
NSEEL_addfunc_retval("eval",1,NSEEL_PProc_THIS,&_eel_eval);
}

#ifdef EEL_WANT_DOCUMENTATION
static const char *eel_eval_function_reference =
"eval\t\"code\"\tExecutes code passed in. Code can use functions, but functions created in code can't be used elsewhere.\0"
;
#endif

#endif

+ 396
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_fft.h View File

@@ -0,0 +1,396 @@
#ifndef __EEL_FFT_H_
#define __EEL_FFT_H_

#include "../fft.h"
#if WDL_FFT_REALSIZE != EEL_F_SIZE
#error WDL_FFT_REALSIZE -- EEL_F_SIZE size mismatch
#endif

#ifndef EEL_FFT_MINBITLEN
#define EEL_FFT_MINBITLEN 4
#endif

#ifndef EEL_FFT_MAXBITLEN
#define EEL_FFT_MAXBITLEN 15
#endif

#ifndef EEL_FFT_MINBITLEN_REORDER
#define EEL_FFT_MINBITLEN_REORDER (EEL_FFT_MINBITLEN-1)
#endif

//#define EEL_SUPER_FAST_FFT_REORDERING // quite a bit faster (50-100%) than "normal", but uses a 256kb lookup
//#define EEL_SLOW_FFT_REORDERING // 20%-80% slower than normal, alloca() use, no reason to ever use this

#ifdef EEL_SUPER_FAST_FFT_REORDERING
static int *fft_reorder_table_for_bitsize(int bitsz)
{
static int s_tab[ (2 << EEL_FFT_MAXBITLEN) + 24*(EEL_FFT_MAXBITLEN-EEL_FFT_MINBITLEN_REORDER+1) ]; // big 256kb table, ugh
if (bitsz<=EEL_FFT_MINBITLEN_REORDER) return s_tab;
return s_tab + (1<<bitsz) + (bitsz-EEL_FFT_MINBITLEN_REORDER) * 24;
}
static void fft_make_reorder_table(int bitsz, int *tab)
{
const int fft_sz=1<<bitsz;
char flag[1<<EEL_FFT_MAXBITLEN];
int x;
int *tabstart = tab;
memset(flag,0,fft_sz);

for (x=0;x<fft_sz;x++)
{
int fx;
if (!flag[x] && (fx=WDL_fft_permute(fft_sz,x))!=x)
{
flag[x]=1;
*tab++ = x;
do
{
flag[fx]=1;
*tab++ = fx;
fx = WDL_fft_permute(fft_sz, fx);
}
while (fx != x);
*tab++ = 0; // delimit a run
}
else flag[x]=1;
}
*tab++ = 0; // doublenull terminated
}

static void fft_reorder_buffer(int bitsz, WDL_FFT_COMPLEX *data, int fwd)
{
const int *tab=fft_reorder_table_for_bitsize(bitsz);
if (!fwd)
{
while (*tab)
{
const int sidx=*tab++;
WDL_FFT_COMPLEX a=data[sidx];
for (;;)
{
WDL_FFT_COMPLEX ta;
const int idx=*tab++;
if (!idx) break;
ta=data[idx];
data[idx]=a;
a=ta;
}
data[sidx] = a;
}
}
else
{
while (*tab)
{
const int sidx=*tab++;
int lidx = sidx;
const WDL_FFT_COMPLEX sta=data[lidx];
for (;;)
{
const int idx=*tab++;
if (!idx) break;

data[lidx]=data[idx];
lidx=idx;
}
data[lidx] = sta;
}
}
return 1;
}
#else
#ifndef EEL_SLOW_FFT_REORDERING
// moderate speed mode, minus the big 256k table

static void fft_reorder_buffer(int bitsz, WDL_FFT_COMPLEX *data, int fwd)
{
// this is a good compromise, quite a bit faster than out of place reordering, but no separate 256kb lookup required
/*
these generated via:
static void fft_make_reorder_table(int bitsz)
{
int fft_sz=1<<bitsz,x;
char flag[65536]={0,};
printf("static const int tab%d[]={ ",fft_sz);
for (x=0;x<fft_sz;x++)
{
int fx;
if (!flag[x] && (fx=WDL_fft_permute(fft_sz,x))!=x)
{
printf("%d, ",x);
do { flag[fx]=1; fx = WDL_fft_permute(fft_sz, fx); } while (fx != x);
}
flag[x]=1;
}
printf(" 0 };\n");
}
*/

static const int tab4_8_32[]={ 1, 0 };
static const int tab16[]={ 1, 3, 0 };
static const int tab64[]={ 1, 3, 9, 0 };
static const int tab128[]={ 1, 3, 4, 9, 14, 0 };
static const int tab256[]={ 1, 3, 6, 12, 13, 14, 19, 0 };
static const int tab512[]={ 1, 4, 7, 9, 18, 50, 115, 0 };
static const int tab1024[]={ 1, 3, 4, 25, 26, 77, 79, 0 };
static const int tab2048[]={ 1, 58, 59, 106, 135, 206, 210, 212, 0 };
static const int tab4096[]={ 1, 3, 12, 25, 54, 221, 313, 431, 453, 0 };
static const int tab8192[]={ 1, 12, 18, 26, 30, 100, 101, 106, 113, 144, 150, 237, 244, 247, 386, 468, 513, 1210, 4839, 0 };
static const int tab16384[]={ 1, 3, 6, 24, 1219, 0 };
static const int tab32768[]={ 1, 3, 4, 7, 13, 18, 31, 64, 113, 145, 203, 246, 594, 956, 1871, 2439, 4959, 19175, 0 };
const int *tab;

switch (bitsz)
{
case 1: return; // no reorder necessary
case 2:
case 3:
case 5: tab = tab4_8_32; break;
case 4: tab=tab16; break;
case 6: tab=tab64; break;
case 7: tab=tab128; break;
case 8: tab=tab256; break;
case 9: tab=tab512; break;
case 10: tab=tab1024; break;
case 11: tab=tab2048; break;
case 12: tab=tab4096; break;
case 13: tab=tab8192; break;
case 14: tab=tab16384; break;
case 15: tab=tab32768; break;
default: return; // no reorder possible
}

const int fft_sz=1<<bitsz;
const int *tb2 = WDL_fft_permute_tab(fft_sz);
if (!tb2) return; // ugh

if (!fwd)
{
while (*tab)
{
const int sidx=*tab++;
WDL_FFT_COMPLEX a=data[sidx];
int idx=sidx;
for (;;)
{
WDL_FFT_COMPLEX ta;
idx=tb2[idx];
if (idx==sidx) break;
ta=data[idx];
data[idx]=a;
a=ta;
}
data[sidx] = a;
}
}
else
{
while (*tab)
{
const int sidx=*tab++;
int lidx = sidx;
const WDL_FFT_COMPLEX sta=data[lidx];
for (;;)
{
const int idx=tb2[lidx];
if (idx==sidx) break;

data[lidx]=data[idx];
lidx=idx;
}
data[lidx] = sta;
}
}
}

#endif // not fast ,not slow, just right

#endif

//#define TIMING
//#include "../timing.h"

// 0=fw, 1=iv, 2=fwreal, 3=ireal, 4=permutec, 6=permuter
// low bit: is inverse
// second bit: was isreal, but no longer used
// third bit: is permute
static void FFT(int sizebits, EEL_F *data, int dir)
{
if (dir >= 4 && dir < 8)
{
if (dir == 4 || dir == 5)
{
//timingEnter(0);
#if defined(EEL_SUPER_FAST_FFT_REORDERING) || !defined(EEL_SLOW_FFT_REORDERING)
fft_reorder_buffer(sizebits,(WDL_FFT_COMPLEX*)data,dir==4);
#else
// old blech
const int flen=1<<sizebits;
int x;
EEL_F *tmp=(EEL_F*)alloca(sizeof(EEL_F)*flen*2);
const int flen2=flen+flen;
// reorder entries, now
memcpy(tmp,data,sizeof(EEL_F)*flen*2);

if (dir == 4)
{
for (x = 0; x < flen2; x += 2)
{
int y=WDL_fft_permute(flen,x/2)*2;
data[x]=tmp[y];
data[x+1]=tmp[y+1];
}
}
else
{
for (x = 0; x < flen2; x += 2)
{
int y=WDL_fft_permute(flen,x/2)*2;
data[y]=tmp[x];
data[y+1]=tmp[x+1];
}
}
#endif
//timingLeave(0);
}
}
else if (dir >= 0 && dir < 2)
{
WDL_fft((WDL_FFT_COMPLEX*)data,1<<sizebits,dir&1);
}
else if (dir >= 2 && dir < 4)
{
WDL_real_fft((WDL_FFT_REAL*)data,1<<sizebits,dir&1);
}
}



static EEL_F * fft_func(int dir, EEL_F **blocks, EEL_F *start, EEL_F *length)
{
const int offs = (int)(*start + 0.0001);
const int itemSizeShift=(dir&2)?0:1;
int l=(int)(*length + 0.0001);
int bitl=0;
int ilen;
EEL_F *ptr;
while (l>1 && bitl < EEL_FFT_MAXBITLEN)
{
bitl++;
l>>=1;
}
if (bitl < ((dir&4) ? EEL_FFT_MINBITLEN_REORDER : EEL_FFT_MINBITLEN)) // smallest FFT is 16 item, smallest reorder is 8 item
{
return start;
}
ilen=1<<bitl;


// check to make sure we don't cross a boundary
if (offs/NSEEL_RAM_ITEMSPERBLOCK != (offs + (ilen<<itemSizeShift) - 1)/NSEEL_RAM_ITEMSPERBLOCK)
{
return start;
}

ptr=__NSEEL_RAMAlloc(blocks,offs);
if (!ptr || ptr==&nseel_ramalloc_onfail)
{
return start;
}

FFT(bitl,ptr,dir);

return start;
}

static EEL_F * NSEEL_CGEN_CALL eel_fft(EEL_F **blocks, EEL_F *start, EEL_F *length)
{
return fft_func(0,blocks,start,length);
}

static EEL_F * NSEEL_CGEN_CALL eel_ifft(EEL_F **blocks, EEL_F *start, EEL_F *length)
{
return fft_func(1,blocks,start,length);
}

static EEL_F * NSEEL_CGEN_CALL eel_fft_real(EEL_F **blocks, EEL_F *start, EEL_F *length)
{
return fft_func(2,blocks,start,length);
}

static EEL_F * NSEEL_CGEN_CALL eel_ifft_real(EEL_F **blocks, EEL_F *start, EEL_F *length)
{
return fft_func(3,blocks,start,length);
}

static EEL_F * NSEEL_CGEN_CALL eel_fft_permute(EEL_F **blocks, EEL_F *start, EEL_F *length)
{
return fft_func(4,blocks,start,length);
}

static EEL_F * NSEEL_CGEN_CALL eel_ifft_permute(EEL_F **blocks, EEL_F *start, EEL_F *length)
{
return fft_func(5,blocks,start,length);
}

static EEL_F * NSEEL_CGEN_CALL eel_convolve_c(EEL_F **blocks,EEL_F *dest, EEL_F *src, EEL_F *lenptr)
{
const int dest_offs = (int)(*dest + 0.0001);
const int src_offs = (int)(*src + 0.0001);
const int len = ((int)(*lenptr + 0.0001)) * 2;
EEL_F *srcptr,*destptr;

if (len < 1 || len > NSEEL_RAM_ITEMSPERBLOCK || dest_offs < 0 || src_offs < 0 ||
dest_offs >= NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK || src_offs >= NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK) return dest;
if ((dest_offs&(NSEEL_RAM_ITEMSPERBLOCK-1)) + len > NSEEL_RAM_ITEMSPERBLOCK) return dest;
if ((src_offs&(NSEEL_RAM_ITEMSPERBLOCK-1)) + len > NSEEL_RAM_ITEMSPERBLOCK) return dest;

srcptr = __NSEEL_RAMAlloc(blocks,src_offs);
if (!srcptr || srcptr==&nseel_ramalloc_onfail) return dest;
destptr = __NSEEL_RAMAlloc(blocks,dest_offs);
if (!destptr || destptr==&nseel_ramalloc_onfail) return dest;


WDL_fft_complexmul((WDL_FFT_COMPLEX*)destptr,(WDL_FFT_COMPLEX*)srcptr,(len/2)&~1);

return dest;
}

void EEL_fft_register()
{
WDL_fft_init();
#if defined(EEL_SUPER_FAST_FFT_REORDERING)
if (!fft_reorder_table_for_bitsize(EEL_FFT_MINBITLEN_REORDER)[0])
{
int x;
for (x=EEL_FFT_MINBITLEN_REORDER;x<=EEL_FFT_MAXBITLEN;x++) fft_make_reorder_table(x,fft_reorder_table_for_bitsize(x));
}
#endif
NSEEL_addfunc_retptr("convolve_c",3,NSEEL_PProc_RAM,&eel_convolve_c);
NSEEL_addfunc_retptr("fft",2,NSEEL_PProc_RAM,&eel_fft);
NSEEL_addfunc_retptr("ifft",2,NSEEL_PProc_RAM,&eel_ifft);
NSEEL_addfunc_retptr("fft_real",2,NSEEL_PProc_RAM,&eel_fft_real);
NSEEL_addfunc_retptr("ifft_real",2,NSEEL_PProc_RAM,&eel_ifft_real);
NSEEL_addfunc_retptr("fft_permute",2,NSEEL_PProc_RAM,&eel_fft_permute);
NSEEL_addfunc_retptr("fft_ipermute",2,NSEEL_PProc_RAM,&eel_ifft_permute);
}

#ifdef EEL_WANT_DOCUMENTATION
static const char *eel_fft_function_reference =
"convolve_c\tdest,src,size\tMultiplies each of size complex pairs in dest by the complex pairs in src. Often used for convolution.\0"
"fft\tbuffer,size\tPerforms a FFT on the data in the local memory buffer at the offset specified by the first parameter. The size of the FFT is specified "
"by the second parameter, which must be 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, or 32768. The outputs are permuted, so if "
"you plan to use them in-order, call fft_permute(buffer, size) before and fft_ipermute(buffer,size) after your in-order use. Your inputs or "
"outputs will need to be scaled down by 1/size, if used.\n"
"Note that fft()/ifft() require real / imaginary input pairs, so a 256 point FFT actually works with 512 items.\n"
"Note that fft()/ifft() must NOT cross a 65,536 item boundary, so be sure to specify the offset accordingly.\0"
"ifft\tbuffer,size\tPerform an inverse FFT. For more information see fft().\0"
"fft_real\tbuffer,size\tPerforms an FFT, but takes size input samples and produces size/2 complex output pairs. Usually used along with fft_permute(size/2). Inputs/outputs will need to be scaled by 0.5/size.\0"
"ifft_real\tbuffer,size\tPerforms an inverse FFT, but takes size/2 complex input pairs and produces size real output values. Usually used along with fft_ipermute(size/2).\0"
"fft_permute\tbuffer,size\tPermute the output of fft() to have bands in-order. See fft() for more information.\0"
"fft_ipermute\tbuffer,size\tPermute the input for ifft(), taking bands from in-order to the order ifft() requires. See fft() for more information.\0"
;
#endif


#endif

+ 262
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_files.h View File

@@ -0,0 +1,262 @@
#ifndef __EEL_FILES_H__
#define __EEL_FILES_H__

// should include eel_strings.h before this, probably
//#define EEL_FILE_OPEN(fn,mode) ((instance)opaque)->OpenFile(fn,mode)
//#define EEL_FILE_GETFP(fp) ((instance)opaque)->GetFileFP(fp)
//#define EEL_FILE_CLOSE(fpindex) ((instance)opaque)->CloseFile(fpindex)

static EEL_F NSEEL_CGEN_CALL _eel_fopen(void *opaque, EEL_F *fn_index, EEL_F *mode_index)
{
EEL_STRING_MUTEXLOCK_SCOPE
const char *fn = EEL_STRING_GET_FOR_INDEX(*fn_index,NULL);
const char *mode = EEL_STRING_GET_FOR_INDEX(*mode_index,NULL);
if (!fn || !mode) return 0;
return (EEL_F) EEL_FILE_OPEN(fn,mode);
}
static EEL_F NSEEL_CGEN_CALL _eel_fclose(void *opaque, EEL_F *fpp)
{
EEL_F ret=EEL_FILE_CLOSE((int)*fpp);
#ifdef EEL_STRING_DEBUGOUT
if (ret < 0) EEL_STRING_DEBUGOUT("fclose(): file handle %f not valid",*fpp);
#endif
return ret;
}
static EEL_F NSEEL_CGEN_CALL _eel_fgetc(void *opaque, EEL_F *fpp)
{
EEL_STRING_MUTEXLOCK_SCOPE
FILE *fp = EEL_FILE_GETFP((int)*fpp);
if (fp) return (EEL_F)fgetc(fp);
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("fgetc(): file handle %f not valid",*fpp);
#endif
return -1.0;
}

static EEL_F NSEEL_CGEN_CALL _eel_ftell(void *opaque, EEL_F *fpp)
{
EEL_STRING_MUTEXLOCK_SCOPE
FILE *fp = EEL_FILE_GETFP((int)*fpp);
if (fp) return (EEL_F)ftell(fp);
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("ftell(): file handle %f not valid",*fpp);
#endif
return -1.0;
}
static EEL_F NSEEL_CGEN_CALL _eel_fflush(void *opaque, EEL_F *fpp)
{
EEL_STRING_MUTEXLOCK_SCOPE
FILE *fp = EEL_FILE_GETFP((int)*fpp);
if (fp) { fflush(fp); return 0.0; }
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("fflush(): file handle %f not valid",*fpp);
#endif
return -1.0;
}

static EEL_F NSEEL_CGEN_CALL _eel_feof(void *opaque, EEL_F *fpp)
{
EEL_STRING_MUTEXLOCK_SCOPE
FILE *fp = EEL_FILE_GETFP((int)*fpp);
if (fp) return feof(fp) ? 1.0 : 0.0;
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("feof(): file handle %f not valid",*fpp);
#endif
return -1.0;
}
static EEL_F NSEEL_CGEN_CALL _eel_fseek(void *opaque, EEL_F *fpp, EEL_F *offset, EEL_F *wh)
{
EEL_STRING_MUTEXLOCK_SCOPE
FILE *fp = EEL_FILE_GETFP((int)*fpp);
if (fp) return fseek(fp, (int) *offset, *wh<0 ? SEEK_SET : *wh > 0 ? SEEK_END : SEEK_CUR);
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("fseek(): file handle %f not valid",*fpp);
#endif
return -1.0;
}
static EEL_F NSEEL_CGEN_CALL _eel_fgets(void *opaque, EEL_F *fpp, EEL_F *strOut)
{
EEL_STRING_MUTEXLOCK_SCOPE
EEL_STRING_STORAGECLASS *wr=NULL;
EEL_STRING_GET_FOR_WRITE(*strOut, &wr);

FILE *fp = EEL_FILE_GETFP((int)*fpp);
if (!fp)
{
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("fgets(): file handle %f not valid",*fpp);
#endif
if (wr) wr->Set("");
return 0.0;
}
char buf[16384];
buf[0]=0;
fgets(buf,sizeof(buf),fp);
if (wr)
{
wr->Set(buf);
return (EEL_F)wr->GetLength();
}
else
{
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("fgets: bad destination specifier passed %f, throwing away %d bytes",*strOut, (int)strlen(buf));
#endif
return (int)strlen(buf);
}
}
static EEL_F NSEEL_CGEN_CALL _eel_fread(void *opaque, EEL_F *fpp, EEL_F *strOut, EEL_F *flen)
{
int use_len = (int) *flen;
if (use_len < 1) return 0.0;

EEL_STRING_MUTEXLOCK_SCOPE
EEL_STRING_STORAGECLASS *wr=NULL;
EEL_STRING_GET_FOR_WRITE(*strOut, &wr);
if (!wr)
{
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("fread: bad destination specifier passed %f, not reading %d bytes",*strOut, use_len);
#endif
return -1;
}

FILE *fp = EEL_FILE_GETFP((int)*fpp);
if (!fp)
{
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("fread(): file handle %f not valid",*fpp);
#endif
if (wr) wr->Set("");
return 0.0;
}

wr->SetLen(use_len);
if (wr->GetLength() != use_len)
{
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("fread: error allocating storage for read of %d bytes", use_len);
#endif
return -1.0;
}
use_len = (int)fread((char *)wr->Get(),1,use_len,fp);
wr->SetLen(use_len > 0 ? use_len : 0, true);
return (EEL_F) use_len;
}

static EEL_F NSEEL_CGEN_CALL _eel_fwrite(void *opaque, EEL_F *fpp, EEL_F *strOut, EEL_F *flen)
{
EEL_STRING_MUTEXLOCK_SCOPE
int use_len = (int) *flen;

EEL_STRING_STORAGECLASS *wr=NULL;
const char *str=EEL_STRING_GET_FOR_INDEX(*strOut, &wr);
if (!wr && !str)
{
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("fwrite: bad source specifier passed %f, not writing %d bytes",*strOut, use_len);
#endif
return -1.0;
}
if (!wr)
{
const int ssl = (int)strlen(str);
if (use_len < 1 || use_len > ssl) use_len = ssl;
}
else
{
if (use_len < 1 || use_len > wr->GetLength()) use_len = wr->GetLength();
}

if (use_len < 1) return 0.0;

FILE *fp = EEL_FILE_GETFP((int)*fpp);
if (!fp)
{
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("fwrite(): file handle %f not valid",*fpp);
#endif
return 0.0;
}

return (EEL_F) fwrite(str,1,use_len,fp);
}

static EEL_F NSEEL_CGEN_CALL _eel_fprintf(void *opaque, INT_PTR nparam, EEL_F **parm)
{
if (opaque && nparam > 1)
{
EEL_STRING_MUTEXLOCK_SCOPE
FILE *fp = EEL_FILE_GETFP((int)*(parm[0]));
if (!fp)
{
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("fprintf(): file handle %f not valid",parm[0][0]);
#endif
return 0.0;
}

EEL_STRING_STORAGECLASS *wr_src=NULL;
const char *fmt = EEL_STRING_GET_FOR_INDEX(*(parm[1]),&wr_src);
if (fmt)
{
char buf[16384];
const int len = eel_format_strings(opaque,fmt,wr_src?(fmt+wr_src->GetLength()) : NULL, buf,(int)sizeof(buf),(int)nparam-2,parm+2);

if (len >= 0)
{
return (EEL_F) fwrite(buf,1,len,fp);
}
else
{
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("fprintf: bad format string %s",fmt);
#endif
}
}
else
{
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("fprintf: bad format specifier passed %f",*(parm[1]));
#endif
}
}
return 0.0;
}

void EEL_file_register()
{
NSEEL_addfunc_retval("fopen",2,NSEEL_PProc_THIS,&_eel_fopen);

NSEEL_addfunc_retval("fread",3,NSEEL_PProc_THIS,&_eel_fread);
NSEEL_addfunc_retval("fgets",2,NSEEL_PProc_THIS,&_eel_fgets);
NSEEL_addfunc_retval("fgetc",1,NSEEL_PProc_THIS,&_eel_fgetc);

NSEEL_addfunc_retval("fwrite",3,NSEEL_PProc_THIS,&_eel_fwrite);
NSEEL_addfunc_varparm("fprintf",2,NSEEL_PProc_THIS,&_eel_fprintf);

NSEEL_addfunc_retval("fseek",3,NSEEL_PProc_THIS,&_eel_fseek);
NSEEL_addfunc_retval("ftell",1,NSEEL_PProc_THIS,&_eel_ftell);
NSEEL_addfunc_retval("feof",1,NSEEL_PProc_THIS,&_eel_feof);
NSEEL_addfunc_retval("fflush",1,NSEEL_PProc_THIS,&_eel_fflush);
NSEEL_addfunc_retval("fclose",1,NSEEL_PProc_THIS,&_eel_fclose);
}

#ifdef EEL_WANT_DOCUMENTATION
static const char *eel_file_function_reference =
"fopen\t\"fn\",\"mode\"\tOpens a file \"fn\" with mode \"mode\". For read, use \"r\" or \"rb\", write \"w\" or \"wb\". Returns a positive integer on success.\0"
"fclose\tfp\tCloses a file previously opened with fopen().\0"
"fread\tfp,#str,length\tReads from file fp into #str, up to length bytes. Returns actual length read, or negative if error.\0"
"fgets\tfp,#str\tReads a line from file fp into #str. Returns length of #str read.\0"
"fgetc\tfp\tReads a character from file fp, returns -1 if EOF.\0"
"fwrite\tfp,#str,len\tWrites up to len characters of #str to file fp. If len is less than 1, the full contents of #str will be written. Returns the number of bytes written to file.\0"
"fprintf\tfp,\"format\"[,...]\tFormats a string and writes it to file fp. For more information on format specifiers, see sprintf(). Returns bytes written to file.\0"
"fseek\tfp,offset,whence\tSeeks file fp, offset bytes from whence reference. Whence negative specifies start of file, positive whence specifies end of file, and zero whence specifies current file position.\0"
"ftell\tfp\tRetunrs the current file position.\0"
"feof\tfp\tReturns nonzero if the file fp is at the end of file.\0"
"fflush\tfp\tIf file fp is open for writing, flushes out any buffered data to disk.\0"
;
#endif


#endif

+ 104
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_import.h View File

@@ -0,0 +1,104 @@
/*******************************************************************************************
* imported EEL
******************************************************************************************/

void (*NSEEL_addfunc_ret_type)(const char *name, int np, int ret_type, NSEEL_PPPROC pproc, void *fptr, eel_function_table *destination); // ret_type=-1 for bool, 1 for value, 0 for ptr
void (*NSEEL_addfunc_varparm_ex)(const char *name, int min_np, int want_exact, NSEEL_PPPROC pproc, EEL_F (NSEEL_CGEN_CALL *fptr)(void *, INT_PTR, EEL_F **), eel_function_table *destination);


NSEEL_VMCTX (*NSEEL_VM_alloc)(); // return a handle
void (*NSEEL_VM_SetGRAM)(NSEEL_VMCTX, void **);
void (*NSEEL_VM_free)(NSEEL_VMCTX ctx); // free when done with a VM and ALL of its code have been freed, as well
void (*NSEEL_VM_SetFunctionTable)(NSEEL_VMCTX, eel_function_table *tab); // use NULL to use default (global) table
EEL_F *(*NSEEL_VM_regvar)(NSEEL_VMCTX ctx, const char *name); // register a variable (before compilation)

void (*NSEEL_VM_SetCustomFuncThis)(NSEEL_VMCTX ctx, void *thisptr);
NSEEL_CODEHANDLE (*NSEEL_code_compile_ex)(NSEEL_VMCTX ctx, const char *code, int lineoffs, int flags);
void (*NSEEL_VM_set_var_resolver)(NSEEL_VMCTX ctx, EEL_F *(*res)(void *userctx, const char *name), void *userctx);
char *(*NSEEL_code_getcodeerror)(NSEEL_VMCTX ctx);
void (*NSEEL_code_execute)(NSEEL_CODEHANDLE code);
void (*NSEEL_code_free)(NSEEL_CODEHANDLE code);
EEL_F *(*nseel_int_register_var)(compileContext *ctx, const char *name, int isReg, const char **namePtrOut);
void (*NSEEL_VM_enumallvars)(NSEEL_VMCTX ctx, int (*func)(const char *name, EEL_F *val, void *ctx), void *userctx);
EEL_F *(*NSEEL_VM_getramptr)(NSEEL_VMCTX ctx, unsigned int offs, int *validAmt);
void ** (*eel_gmem_attach)(const char *nm, bool is_alloc);
void (*eel_fft_register)(eel_function_table*);

struct eelStringSegmentRec {
struct eelStringSegmentRec *_next;
const char *str_start; // escaped characters, including opening/trailing characters
int str_len;
};
void (*NSEEL_VM_SetStringFunc)(NSEEL_VMCTX ctx,
EEL_F (*onString)(void *caller_this, struct eelStringSegmentRec *list),
EEL_F (*onNamedString)(void *caller_this, const char *name));

// call with NULL to calculate size, or non-null to generate to buffer (returning size used -- will not null terminate, caller responsibility)
int (*nseel_stringsegments_tobuf)(char *bufOut, int bufout_sz, struct eelStringSegmentRec *list);

void *(*NSEEL_PProc_RAM)(void *data, int data_size, struct _compileContext *ctx);
void *(*NSEEL_PProc_THIS)(void *data, int data_size, struct _compileContext *ctx);

void (*eel_enterfp)(int s[2]);
void (*eel_leavefp)(int s[2]);


eel_function_table g_eel_function_table;
#define NSEEL_ADDFUNC_DESTINATION (&g_eel_function_table)

//
// adds a function that returns a value (EEL_F)
#define NSEEL_addfunc_retval(name,np,pproc,fptr) \
NSEEL_addfunc_ret_type(name,np,1,pproc,(void *)(fptr),NSEEL_ADDFUNC_DESTINATION)

// adds a function that returns a pointer (EEL_F*)
#define NSEEL_addfunc_retptr(name,np,pproc,fptr) \
NSEEL_addfunc_ret_type(name,np,0,pproc,(void *)(fptr),NSEEL_ADDFUNC_DESTINATION)

// adds a void or bool function
#define NSEEL_addfunc_retbool(name,np,pproc,fptr) \
NSEEL_addfunc_ret_type(name,np,-1,pproc,(void *)(fptr),NSEEL_ADDFUNC_DESTINATION)

// adds a function that takes min_np or more parameters (func sig needs to be EEL_F func(void *ctx, INT_PTR np, EEL_F **parms)
#define NSEEL_addfunc_varparm(name, min_np, pproc, fptr) \
NSEEL_addfunc_varparm_ex(name,min_np,0,pproc,fptr,NSEEL_ADDFUNC_DESTINATION)

// adds a function that takes np parameters via func: sig needs to be EEL_F func(void *ctx, INT_PTR np, EEL_F **parms)
#define NSEEL_addfunc_exparms(name, np, pproc, fptr) \
NSEEL_addfunc_varparm_ex(name,np,1,pproc,fptr,NSEEL_ADDFUNC_DESTINATION)

class eel_string_context_state;

#define __NS_EELINT_H__

#define EEL_IMPORT_ALL(IMPORT_FUNC) \
IMPORT_FUNC(NSEEL_addfunc_ret_type) \
IMPORT_FUNC(NSEEL_addfunc_varparm_ex) \
IMPORT_FUNC(NSEEL_VM_free) \
IMPORT_FUNC(NSEEL_VM_SetFunctionTable) \
IMPORT_FUNC(NSEEL_VM_regvar) \
IMPORT_FUNC(NSEEL_VM_SetCustomFuncThis) \
IMPORT_FUNC(NSEEL_code_compile_ex) \
IMPORT_FUNC(NSEEL_code_getcodeerror) \
IMPORT_FUNC(NSEEL_code_execute) \
IMPORT_FUNC(NSEEL_code_free) \
IMPORT_FUNC(NSEEL_PProc_THIS) \
IMPORT_FUNC(NSEEL_PProc_RAM) \
IMPORT_FUNC(NSEEL_VM_SetStringFunc) \
IMPORT_FUNC(NSEEL_VM_enumallvars) \
IMPORT_FUNC(NSEEL_VM_getramptr) \
IMPORT_FUNC(NSEEL_VM_SetGRAM) \
IMPORT_FUNC(eel_gmem_attach) \
IMPORT_FUNC(eel_fft_register) \
IMPORT_FUNC(nseel_stringsegments_tobuf) \
IMPORT_FUNC(nseel_int_register_var) \
IMPORT_FUNC(eel_leavefp) \
IMPORT_FUNC(eel_enterfp) \
IMPORT_FUNC(NSEEL_VM_set_var_resolver) \
IMPORT_FUNC(NSEEL_VM_alloc) /* keep NSEEL_VM_alloc last */

/*******************************************************************************************
* END of imported EEL
******************************************************************************************/

+ 3055
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_lice.h
File diff suppressed because it is too large
View File


+ 782
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_mdct.h View File

@@ -0,0 +1,782 @@
#ifndef _EEL_MDCT_H_
#define _EEL_MDCT_H_


#include "ns-eel-int.h"

#ifdef _WIN32
#include <malloc.h>
#endif
#include <string.h>
#include <stdlib.h>
#include <math.h>

#define EEL_DCT_MINBITLEN 5
#define EEL_DCT_MAXBITLEN 12


#define PI 3.1415926535897932384626433832795

typedef struct {
int n;
int log2n;
EEL_F *trig;
int *bitrev;
EEL_F scale;
EEL_F *window;
} mdct_lookup;

static void mdct(EEL_F *in, EEL_F *out, int len)
{
int k;
EEL_F pioverlen = PI * 0.5 / (EEL_F)len;
for (k = 0; k < len / 2; k ++)
{
int i;
EEL_F d = 0.0;
for (i = 0; i < len; i ++)
{
d += in[i] * cos(pioverlen * (2.0 * i + 1.0 + len * 0.5) * (2.0 * k + 1.0));
}
out[k] = (EEL_F)d;
}
}

static void imdct(EEL_F *in, EEL_F *out, int len)
{
int k;
EEL_F fourovern = 4.0 / (EEL_F)len;
EEL_F pioverlen = PI * 0.5 / (EEL_F)len;
for (k = 0; k < len; k ++)
{
int i;
EEL_F d = 0.0;
for (i = 0; i < len / 2; i ++)
{
d += in[i] * cos(pioverlen * (2.0 * k + 1.0 + len * 0.5) * (2 * i + 1.0));
}
out[k] = (EEL_F)(d * fourovern);
}
}


// MDCT/iMDCT borrowed from Vorbis, thanks xiph!


#define cPI3_8 .38268343236508977175
#define cPI2_8 .70710678118654752441
#define cPI1_8 .92387953251128675613

#define FLOAT_CONV(x) ((EEL_F) ( x ))
#define MULT_NORM(x) (x)
#define HALVE(x) ((x)*.5f)



/* 8 point butterfly (in place, 4 register) */
static void mdct_butterfly_8(EEL_F *x) {
EEL_F r0 = x[6] + x[2];
EEL_F r1 = x[6] - x[2];
EEL_F r2 = x[4] + x[0];
EEL_F r3 = x[4] - x[0];

x[6] = r0 + r2;
x[4] = r0 - r2;

r0 = x[5] - x[1];
r2 = x[7] - x[3];
x[0] = r1 + r0;
x[2] = r1 - r0;

r0 = x[5] + x[1];
r1 = x[7] + x[3];
x[3] = r2 + r3;
x[1] = r2 - r3;
x[7] = r1 + r0;
x[5] = r1 - r0;

}

/* 16 point butterfly (in place, 4 register) */
static void mdct_butterfly_16(EEL_F *x) {
EEL_F r0 = x[1] - x[9];
EEL_F r1 = x[0] - x[8];

x[8] += x[0];
x[9] += x[1];
x[0] = MULT_NORM((r0 + r1) * cPI2_8);
x[1] = MULT_NORM((r0 - r1) * cPI2_8);

r0 = x[3] - x[11];
r1 = x[10] - x[2];
x[10] += x[2];
x[11] += x[3];
x[2] = r0;
x[3] = r1;

r0 = x[12] - x[4];
r1 = x[13] - x[5];
x[12] += x[4];
x[13] += x[5];
x[4] = MULT_NORM((r0 - r1) * cPI2_8);
x[5] = MULT_NORM((r0 + r1) * cPI2_8);

r0 = x[14] - x[6];
r1 = x[15] - x[7];
x[14] += x[6];
x[15] += x[7];
x[6] = r0;
x[7] = r1;

mdct_butterfly_8(x);
mdct_butterfly_8(x + 8);
}

/* 32 point butterfly (in place, 4 register) */
static void mdct_butterfly_32(EEL_F *x) {
EEL_F r0 = x[30] - x[14];
EEL_F r1 = x[31] - x[15];

x[30] += x[14];
x[31] += x[15];
x[14] = r0;
x[15] = r1;

r0 = x[28] - x[12];
r1 = x[29] - x[13];
x[28] += x[12];
x[29] += x[13];
x[12] = MULT_NORM( r0 * cPI1_8 - r1 * cPI3_8 );
x[13] = MULT_NORM( r0 * cPI3_8 + r1 * cPI1_8 );

r0 = x[26] - x[10];
r1 = x[27] - x[11];
x[26] += x[10];
x[27] += x[11];
x[10] = MULT_NORM(( r0 - r1 ) * cPI2_8);
x[11] = MULT_NORM(( r0 + r1 ) * cPI2_8);

r0 = x[24] - x[8];
r1 = x[25] - x[9];
x[24] += x[8];
x[25] += x[9];
x[8] = MULT_NORM( r0 * cPI3_8 - r1 * cPI1_8 );
x[9] = MULT_NORM( r1 * cPI3_8 + r0 * cPI1_8 );

r0 = x[22] - x[6];
r1 = x[7] - x[23];
x[22] += x[6];
x[23] += x[7];
x[6] = r1;
x[7] = r0;

r0 = x[4] - x[20];
r1 = x[5] - x[21];
x[20] += x[4];
x[21] += x[5];
x[4] = MULT_NORM( r1 * cPI1_8 + r0 * cPI3_8 );
x[5] = MULT_NORM( r1 * cPI3_8 - r0 * cPI1_8 );

r0 = x[2] - x[18];
r1 = x[3] - x[19];
x[18] += x[2];
x[19] += x[3];
x[2] = MULT_NORM(( r1 + r0 ) * cPI2_8);
x[3] = MULT_NORM(( r1 - r0 ) * cPI2_8);

r0 = x[0] - x[16];
r1 = x[1] - x[17];
x[16] += x[0];
x[17] += x[1];
x[0] = MULT_NORM( r1 * cPI3_8 + r0 * cPI1_8 );
x[1] = MULT_NORM( r1 * cPI1_8 - r0 * cPI3_8 );

mdct_butterfly_16(x);
mdct_butterfly_16(x + 16);

}

/* N point first stage butterfly (in place, 2 register) */
static void mdct_butterfly_first(EEL_F *T,
EEL_F *x,
int points) {

EEL_F *x1 = x + points - 8;
EEL_F *x2 = x + (points >> 1) - 8;
EEL_F r0;
EEL_F r1;

do {

r0 = x1[6] - x2[6];
r1 = x1[7] - x2[7];
x1[6] += x2[6];
x1[7] += x2[7];
x2[6] = MULT_NORM(r1 * T[1] + r0 * T[0]);
x2[7] = MULT_NORM(r1 * T[0] - r0 * T[1]);

r0 = x1[4] - x2[4];
r1 = x1[5] - x2[5];
x1[4] += x2[4];
x1[5] += x2[5];
x2[4] = MULT_NORM(r1 * T[5] + r0 * T[4]);
x2[5] = MULT_NORM(r1 * T[4] - r0 * T[5]);

r0 = x1[2] - x2[2];
r1 = x1[3] - x2[3];
x1[2] += x2[2];
x1[3] += x2[3];
x2[2] = MULT_NORM(r1 * T[9] + r0 * T[8]);
x2[3] = MULT_NORM(r1 * T[8] - r0 * T[9]);

r0 = x1[0] - x2[0];
r1 = x1[1] - x2[1];
x1[0] += x2[0];
x1[1] += x2[1];
x2[0] = MULT_NORM(r1 * T[13] + r0 * T[12]);
x2[1] = MULT_NORM(r1 * T[12] - r0 * T[13]);

x1 -= 8;
x2 -= 8;
T += 16;

} while(x2 >= x);
}

/* N/stage point generic N stage butterfly (in place, 2 register) */
static void mdct_butterfly_generic(EEL_F *T,
EEL_F *x,
int points,
int trigint) {

EEL_F *x1 = x + points - 8;
EEL_F *x2 = x + (points >> 1) - 8;
EEL_F r0;
EEL_F r1;

do {

r0 = x1[6] - x2[6];
r1 = x1[7] - x2[7];
x1[6] += x2[6];
x1[7] += x2[7];
x2[6] = MULT_NORM(r1 * T[1] + r0 * T[0]);
x2[7] = MULT_NORM(r1 * T[0] - r0 * T[1]);

T += trigint;

r0 = x1[4] - x2[4];
r1 = x1[5] - x2[5];
x1[4] += x2[4];
x1[5] += x2[5];
x2[4] = MULT_NORM(r1 * T[1] + r0 * T[0]);
x2[5] = MULT_NORM(r1 * T[0] - r0 * T[1]);

T += trigint;

r0 = x1[2] - x2[2];
r1 = x1[3] - x2[3];
x1[2] += x2[2];
x1[3] += x2[3];
x2[2] = MULT_NORM(r1 * T[1] + r0 * T[0]);
x2[3] = MULT_NORM(r1 * T[0] - r0 * T[1]);

T += trigint;

r0 = x1[0] - x2[0];
r1 = x1[1] - x2[1];
x1[0] += x2[0];
x1[1] += x2[1];
x2[0] = MULT_NORM(r1 * T[1] + r0 * T[0]);
x2[1] = MULT_NORM(r1 * T[0] - r0 * T[1]);

T += trigint;
x1 -= 8;
x2 -= 8;

} while(x2 >= x);
}

static void mdct_butterflies(mdct_lookup *init,
EEL_F *x,
int points) {

EEL_F *T = init->trig;
int stages = init->log2n - 5;
int i, j;

if(--stages > 0) {
mdct_butterfly_first(T, x, points);
}

for(i = 1; --stages > 0; i++) {
for(j = 0; j < (1 << i); j++)
mdct_butterfly_generic(T, x + (points >> i)*j, points >> i, 4 << i);
}

for(j = 0; j < points; j += 32)
mdct_butterfly_32(x + j);

}

static void mdct_bitreverse(mdct_lookup *init,
EEL_F *x) {
int n = init->n;
int *bit = init->bitrev;
EEL_F *w0 = x;
EEL_F *w1 = x = w0 + (n >> 1);
EEL_F *T = init->trig + n;

do {
EEL_F *x0 = x + bit[0];
EEL_F *x1 = x + bit[1];

EEL_F r0 = x0[1] - x1[1];
EEL_F r1 = x0[0] + x1[0];
EEL_F r2 = MULT_NORM(r1 * T[0] + r0 * T[1]);
EEL_F r3 = MULT_NORM(r1 * T[1] - r0 * T[0]);

w1 -= 4;

r0 = HALVE(x0[1] + x1[1]);
r1 = HALVE(x0[0] - x1[0]);

w0[0] = r0 + r2;
w1[2] = r0 - r2;
w0[1] = r1 + r3;
w1[3] = r3 - r1;

x0 = x + bit[2];
x1 = x + bit[3];

r0 = x0[1] - x1[1];
r1 = x0[0] + x1[0];
r2 = MULT_NORM(r1 * T[2] + r0 * T[3]);
r3 = MULT_NORM(r1 * T[3] - r0 * T[2]);

r0 = HALVE(x0[1] + x1[1]);
r1 = HALVE(x0[0] - x1[0]);

w0[2] = r0 + r2;
w1[0] = r0 - r2;
w0[3] = r1 + r3;
w1[1] = r3 - r1;

T += 4;
bit += 4;
w0 += 4;

} while(w0 < w1);
}

static void megabuf_mdct_apply_window(void *init, EEL_F *inbuf, EEL_F *outbuf)
{
mdct_lookup *p = (mdct_lookup *)init;
EEL_F *w;
int cnt;
if (!p) return;

w = p->window;
if (!w) return;

cnt = p->n / 2;
while (cnt--) *outbuf++ = *inbuf++ * *w++;
cnt = p->n / 2;
while (cnt--) *outbuf++ = *inbuf++ * *--w;
}



static void *megabuf_mdct_init(int n) {
mdct_lookup *lookup = (mdct_lookup *)calloc(sizeof(mdct_lookup), 1);
int i;
EEL_F c = (PI / (EEL_F) n);
int *bitrev;
EEL_F *T;
int n2, log2n;
if (!lookup) return 0;

lookup->n = n;
lookup->window = (EEL_F *)calloc(sizeof(EEL_F), n / 2);
if (!lookup->window) return lookup;

for (i = 0; i < n / 2; i ++)
{
lookup->window[i] = sin(c * (i + 0.5));
}

if (n <= 32) return lookup;
bitrev = (int*)calloc(sizeof(int), (n / 4));
lookup->bitrev = bitrev;
if (!bitrev) return lookup;

T = (EEL_F*)calloc(sizeof(EEL_F), (n + n / 4));
lookup->trig = T;
if (!T) return lookup;

n2 = n >> 1;
log2n = lookup->log2n = (int)(log((double)n) / log(2.0) + 0.5);

/* trig lookups... */

for(i = 0; i < n / 4; i++) {
T[i * 2] = FLOAT_CONV(cos((PI / n) * (4 * i)));
T[i * 2 + 1] = FLOAT_CONV(-sin((PI / n) * (4 * i)));
T[n2 + i * 2] = FLOAT_CONV(cos((PI / (2 * n)) * (2 * i + 1)));
T[n2 + i * 2 + 1] = FLOAT_CONV(sin((PI / (2 * n)) * (2 * i + 1)));
}
for(i = 0; i < n / 8; i++) {
T[n + i * 2] = FLOAT_CONV(cos((PI / n) * (4 * i + 2)) * .5);
T[n + i * 2 + 1] = FLOAT_CONV(-sin((PI / n) * (4 * i + 2)) * .5);
}

/* bitreverse lookup... */

{
int mask = (1 << (log2n - 1)) - 1, j;
int msb = 1 << (log2n - 2);
for(i = 0; i < n / 8; i++) {
int acc = 0;
for(j = 0; msb >> j; j++)
if((msb >> j)&i)acc |= 1 << j;
bitrev[i * 2] = ((~acc)&mask) - 1;
bitrev[i * 2 + 1] = acc;

}
}
lookup->scale = FLOAT_CONV(4.f / n);
return lookup;
}

static void megabuf_mdct_backward(void *init, EEL_F *in, EEL_F *out) {
mdct_lookup *lookup = (mdct_lookup *)init;
int n, n2, n4;
EEL_F *iX, *oX, *T;
if (!lookup) return;
n = lookup->n;
if (n <= 32 || !lookup->bitrev || !lookup->trig)
{
imdct(in, out, n);
return;
}
n2 = n >> 1;
n4 = n >> 2;

/* rotate */

iX = in + n2 - 7;
oX = out + n2 + n4;
T = lookup->trig + n4;

do {
oX -= 4;
oX[0] = MULT_NORM(-iX[2] * T[3] - iX[0] * T[2]);
oX[1] = MULT_NORM (iX[0] * T[3] - iX[2] * T[2]);
oX[2] = MULT_NORM(-iX[6] * T[1] - iX[4] * T[0]);
oX[3] = MULT_NORM (iX[4] * T[1] - iX[6] * T[0]);
iX -= 8;
T += 4;
} while(iX >= in);

iX = in + n2 - 8;
oX = out + n2 + n4;
T = lookup->trig + n4;

do {
T -= 4;
oX[0] = MULT_NORM (iX[4] * T[3] + iX[6] * T[2]);
oX[1] = MULT_NORM (iX[4] * T[2] - iX[6] * T[3]);
oX[2] = MULT_NORM (iX[0] * T[1] + iX[2] * T[0]);
oX[3] = MULT_NORM (iX[0] * T[0] - iX[2] * T[1]);
iX -= 8;
oX += 4;
} while(iX >= in);

mdct_butterflies(lookup, out + n2, n2);
mdct_bitreverse(lookup, out);

/* roatate + window */

{
EEL_F *oX1 = out + n2 + n4;
EEL_F *oX2 = out + n2 + n4;
iX = out;
T = lookup->trig + n2;

do {
oX1 -= 4;

oX1[3] = MULT_NORM (iX[0] * T[1] - iX[1] * T[0]);
oX2[0] = -MULT_NORM (iX[0] * T[0] + iX[1] * T[1]);

oX1[2] = MULT_NORM (iX[2] * T[3] - iX[3] * T[2]);
oX2[1] = -MULT_NORM (iX[2] * T[2] + iX[3] * T[3]);

oX1[1] = MULT_NORM (iX[4] * T[5] - iX[5] * T[4]);
oX2[2] = -MULT_NORM (iX[4] * T[4] + iX[5] * T[5]);

oX1[0] = MULT_NORM (iX[6] * T[7] - iX[7] * T[6]);
oX2[3] = -MULT_NORM (iX[6] * T[6] + iX[7] * T[7]);

oX2 += 4;
iX += 8;
T += 8;
} while(iX < oX1);

iX = out + n2 + n4;
oX1 = out + n4;
oX2 = oX1;

do {
oX1 -= 4;
iX -= 4;

oX2[0] = -(oX1[3] = iX[3]);
oX2[1] = -(oX1[2] = iX[2]);
oX2[2] = -(oX1[1] = iX[1]);
oX2[3] = -(oX1[0] = iX[0]);

oX2 += 4;
} while(oX2 < iX);

iX = out + n2 + n4;
oX1 = out + n2 + n4;
oX2 = out + n2;
do {
oX1 -= 4;
oX1[0] = iX[3];
oX1[1] = iX[2];
oX1[2] = iX[1];
oX1[3] = iX[0];
iX += 4;
} while(oX1 > oX2);
}
}


static void megabuf_mdct_forward(void *init, EEL_F *in, EEL_F *out) {
mdct_lookup *lookup = (mdct_lookup *)init;
int n, n2, n4, n8;
EEL_F *w, *w2;
if (!lookup) return;

n = lookup->n;
if (n <= 32 || !lookup->bitrev || !lookup->trig)
{
mdct(in, out, n);
return;
}
n2 = n >> 1;
n4 = n >> 2;
n8 = n >> 3;
EEL_F oldw[1<<EEL_DCT_MAXBITLEN];
w = oldw;
w2 = w + n2;

/* rotate */

/* window + rotate + step 1 */

{
EEL_F r0;
EEL_F r1;
EEL_F *x0 = in + n2 + n4;
EEL_F *x1 = x0 + 1;
EEL_F *T = lookup->trig + n2;

int i = 0;

for(i = 0; i < n8; i += 2) {
x0 -= 4;
T -= 2;
r0 = x0[2] + x1[0];
r1 = x0[0] + x1[2];
w2[i] = MULT_NORM(r1 * T[1] + r0 * T[0]);
w2[i + 1] = MULT_NORM(r1 * T[0] - r0 * T[1]);
x1 += 4;
}

x1 = in + 1;

for(; i < n2 - n8; i += 2) {
T -= 2;
x0 -= 4;
r0 = x0[2] - x1[0];
r1 = x0[0] - x1[2];
w2[i] = MULT_NORM(r1 * T[1] + r0 * T[0]);
w2[i + 1] = MULT_NORM(r1 * T[0] - r0 * T[1]);
x1 += 4;
}

x0 = in + n;

for(; i < n2; i += 2) {
T -= 2;
x0 -= 4;
r0 = -x0[2] - x1[0];
r1 = -x0[0] - x1[2];
w2[i] = MULT_NORM(r1 * T[1] + r0 * T[0]);
w2[i + 1] = MULT_NORM(r1 * T[0] - r0 * T[1]);
x1 += 4;
}


mdct_butterflies(lookup, w + n2, n2);
mdct_bitreverse(lookup, w);

/* roatate + window */

T = lookup->trig + n2;
x0 = out + n2;

for(i = 0; i < n4; i++) {
x0--;
out[i] = MULT_NORM((w[0] * T[0] + w[1] * T[1]) * lookup->scale);
x0[0] = MULT_NORM((w[0] * T[1] - w[1] * T[0]) * lookup->scale);
w += 2;
T += 2;
}
}
}

#if 0

static void dct(EEL_F *in, EEL_F *out, int len)
{
int k;
EEL_F wk = sqrt(2.0 / len);
EEL_F overtwolen = 0.5 / (EEL_F)len;
for (k = 0; k < len; k ++)
{
int n;
EEL_F d = 0.0;
for (n = 0; n < len; n ++)
{
int an = n + 1;
d += in[n] * cos(PI * (2.0 * n + 1.0) * (EEL_F)k * overtwolen);
}
if (!k) d /= sqrt(len);
else d *= wk;
out[k] = (EEL_F)d;
}
}


static void idct(EEL_F *in, EEL_F *out, int len)
{
int n;
EEL_F dd0 = 1.0 / sqrt(len);
EEL_F dd1 = sqrt(2.0 / len);
EEL_F overtwolen = 0.5 / len;
for (n = 0; n < len; n ++)
{
int k;
EEL_F d = 0.0;
for (k = 0; k < len; k ++)
{
EEL_F dd;
if (!k) dd = dd0 * in[k];
else dd = dd1 * in[k];
d += dd * cos(PI * (2.0 * n + 1.0) * k * overtwolen);
}
out[n] = (EEL_F)d;
}
}
#endif


// 0 is megabuf blocks
// 1 is need_free flag


static EEL_F * NSEEL_CGEN_CALL mdct_func(int dir, EEL_F **blocks, EEL_F *start, EEL_F *length)
{
int l = (int)(*length + 0.0001);
int offs = (int)(*start + 0.0001);
int bitl = 0;
int ilen;
int bidx;
EEL_F *ptr;
while (l > 1 && bitl < EEL_DCT_MAXBITLEN)
{
bitl++;
l >>= 1;
}
if (bitl < EEL_DCT_MINBITLEN)
{
return start;
}
ilen = 1 << bitl;

bidx = bitl - EEL_DCT_MINBITLEN;


// check to make sure we don't cross a boundary
if (offs / NSEEL_RAM_ITEMSPERBLOCK != (offs + ilen * 2 - 1) / NSEEL_RAM_ITEMSPERBLOCK)
{
return start;
}

ptr = __NSEEL_RAMAlloc(blocks, offs);
if (!ptr || ptr == &nseel_ramalloc_onfail)
{
return start;
}

if (ilen > 1)
{
static void *mdct_ctxs[1 + EEL_DCT_MAXBITLEN - EEL_DCT_MINBITLEN];

if (!mdct_ctxs[bidx])
{
NSEEL_HOSTSTUB_EnterMutex();
if (!mdct_ctxs[bidx])
mdct_ctxs[bidx] = megabuf_mdct_init(ilen);
NSEEL_HOSTSTUB_LeaveMutex();
}

if (mdct_ctxs[bidx])
{
EEL_F buf[1 << EEL_DCT_MAXBITLEN];
if (dir < 0)
{
megabuf_mdct_backward(mdct_ctxs[bidx], ptr, buf);
megabuf_mdct_apply_window(mdct_ctxs[bidx], buf, ptr);
}
else
{
megabuf_mdct_apply_window(mdct_ctxs[bidx], ptr, buf);
megabuf_mdct_forward(mdct_ctxs[bidx], buf, ptr);
}
}
}
return start;
}



static EEL_F * NSEEL_CGEN_CALL megabuf_mdct(EEL_F **blocks, EEL_F *start, EEL_F *length)
{
return mdct_func(0, blocks, start, length);
}

static EEL_F * NSEEL_CGEN_CALL megabuf_imdct(EEL_F **blocks, EEL_F *start, EEL_F *length)
{
return mdct_func(-1, blocks, start, length);
}

void EEL_mdct_register()
{
NSEEL_addfunc_retptr("mdct", 2, NSEEL_PProc_RAM, &megabuf_mdct);
NSEEL_addfunc_retptr("imdct", 2, NSEEL_PProc_RAM, &megabuf_imdct);
}

#ifdef EEL_WANT_DOCUMENTATION
static const char *eel_mdct_function_reference =
"mdct\tbuffer,length\tPerforms a windowed modified DCT, taking length inputs and producing length/2 outputs. buffer must not cross a 65,536 item boundary, and length must be 64, 128, 256, 512, 2048 or 4096.\0"
"imdct\tbuffer,length\tPerforms a windowed inverse modified DCT, taking length/2 inputs and producing length outputs. buffer must not cross a 65,536 item boundary, and length must be 64, 128, 256, 512, 2048 or 4096.\0"
;
#endif


#endif

+ 73
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_misc.h View File

@@ -0,0 +1,73 @@
#ifndef _EEL_MISC_H_
#define _EEL_MISC_H_


#ifndef _WIN32
#include <sys/time.h>
#endif
#include <time.h>
// some generic EEL functions for things like time

#ifndef EEL_MISC_NO_SLEEP
static EEL_F NSEEL_CGEN_CALL _eel_sleep(void *opaque, EEL_F *amt)
{
if (*amt >= 0.0)
{
#ifdef _WIN32
if (*amt > 30000000.0) Sleep(30000000);
else Sleep((DWORD)(*amt+0.5));
#else
if (*amt > 30000000.0) usleep(((useconds_t)30000000)*1000);
else usleep((useconds_t)(*amt*1000.0+0.5));
#endif
}
return 0.0;
}
#endif

static EEL_F * NSEEL_CGEN_CALL _eel_time(void *opaque, EEL_F *v)
{
*v = (EEL_F) time(NULL);
return v;
}

static EEL_F * NSEEL_CGEN_CALL _eel_time_precise(void *opaque, EEL_F *v)
{
#ifdef _WIN32
LARGE_INTEGER freq,now;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&now);
*v = (double)now.QuadPart / (double)freq.QuadPart;
// *v = (EEL_F)timeGetTime() * 0.001;
#else
struct timeval tm={0,};
gettimeofday(&tm,NULL);
*v = tm.tv_sec + tm.tv_usec*0.000001;
#endif
return v;
}


void EEL_misc_register()
{
#ifndef EEL_MISC_NO_SLEEP
NSEEL_addfunc_retval("sleep",1,NSEEL_PProc_THIS,&_eel_sleep);
#endif
NSEEL_addfunc_retptr("time",1,NSEEL_PProc_THIS,&_eel_time);
NSEEL_addfunc_retptr("time_precise",1,NSEEL_PProc_THIS,&_eel_time_precise);
}

#ifdef EEL_WANT_DOCUMENTATION
static const char *eel_misc_function_reference =
#ifndef EEL_MISC_NO_SLEEP
"sleep\tms\tYields the CPU for the millisecond count specified, calling Sleep() on Windows or usleep() on other platforms.\0"
#endif
"time\t[&val]\tSets the parameter (or a temporary buffer if omitted) to the number of seconds since January 1, 1970, and returns a reference to that value. "
"The granularity of the value returned is 1 second.\0"
"time_precise\t[&val]\tSets the parameter (or a temporary buffer if omitted) to a system-local timestamp in seconds, and returns a reference to that value. "
"The granularity of the value returned is system defined (but generally significantly smaller than one second).\0"
;
#endif


#endif

+ 564
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_net.h View File

@@ -0,0 +1,564 @@
#ifndef _EEL_NET_H_
#define _EEL_NET_H_

// x = tcp_listen(port[,interface, connected_ip_out]) poll this, returns connection id > 0, or <0 on error, or 0 if no new connect -- interface only valid on first call (or after tcp_listen_end(port))
// tcp_listen_end(port);

// connection = tcp_connect(host, port[, block]) // connection id > 0 on ok
// tcp_set_block(connection, block?)
// tcp_close(connection)

// tcp_send(connection, string[, length]) // can return 0 if block, -1 if error, otherwise returns length sent
// tcp_recv(connection, string[, maxlength]) // 0 on nothing, -1 on error, otherwise returns length recv'd


// need:
// #define EEL_NET_GET_CONTEXT(opaque) (((sInst *)opaque)->m_net_state)

// you must pass a JNL_AsyncDNS object to eel_net_state to support nonblocking connect with DNS resolution, otherwise DNS will block
// #define EEL_NET_NO_SYNC_DNS -- never ever call gethostbyname() synchronously, may disable DNS for blocking connect, or if a JNL_IAsyncDNS is not provided.

#ifndef EEL_NET_MAXSEND
#define EEL_NET_MAXSEND (EEL_STRING_MAXUSERSTRING_LENGTH_HINT+4096)
#endif


#include "../jnetlib/netinc.h"
#define JNL_NO_IMPLEMENTATION
#include "../jnetlib/asyncdns.h"

class eel_net_state
{
public:
enum { STATE_FREE=0, STATE_RESOLVING, STATE_CONNECTED, STATE_ERR };
enum { CONNECTION_ID_BASE=0x110000 };

eel_net_state(int max_con, JNL_IAsyncDNS *dns);
~eel_net_state();

struct connection_state {
char *hostname; // set during resolve only
SOCKET sock;
int state; // STATE_RESOLVING...
int port;
bool blockmode;
};
WDL_TypedBuf<connection_state> m_cons;
WDL_IntKeyedArray<SOCKET> m_listens;
JNL_IAsyncDNS *m_dns;

EEL_F onConnect(char *hostNameOwned, int port, int block);
EEL_F onClose(void *opaque, EEL_F handle);
EEL_F set_block(void *opaque, EEL_F handle, bool block);
EEL_F onListen(void *opaque, EEL_F handle, int mode, EEL_F *ifStr, EEL_F *ipOut);

int __run_connect(connection_state *cs, unsigned int ip);
int __run(connection_state *cs);
int do_send(void *opaque, EEL_F h, const char *src, int len);
int do_recv(void *opaque, EEL_F h, char *buf, int maxlen);
#ifdef _WIN32
bool m_had_socketlib_init;
#endif
};

eel_net_state::eel_net_state(int max_con, JNL_IAsyncDNS *dns)
{
#ifdef _WIN32
m_had_socketlib_init=false;
#endif

m_cons.Resize(max_con);
int x;
for (x=0;x<m_cons.GetSize();x++)
{
m_cons.Get()[x].state = STATE_FREE;
m_cons.Get()[x].sock = INVALID_SOCKET;
m_cons.Get()[x].hostname = NULL;
}
m_dns=dns;
}

eel_net_state::~eel_net_state()
{
int x;
for (x=0;x<m_cons.GetSize();x++)
{
SOCKET s=m_cons.Get()[x].sock;
if (s != INVALID_SOCKET)
{
shutdown(s,SHUT_RDWR);
closesocket(s);
}
free(m_cons.Get()[x].hostname);
}
for (x=0;x<m_listens.GetSize();x++)
{
SOCKET s=m_listens.Enumerate(x);
shutdown(s, SHUT_RDWR);
closesocket(s);
}
}
EEL_F eel_net_state::onConnect(char *hostNameOwned, int port, int block)
{
int x;
#ifdef _WIN32
if (!m_had_socketlib_init)
{
m_had_socketlib_init=1;
WSADATA wsaData;
WSAStartup(MAKEWORD(1, 1), &wsaData);
}
#endif
for(x=0;x<m_cons.GetSize();x++)
{
connection_state *s=m_cons.Get()+x;
if (s->state == STATE_FREE)
{
unsigned int ip=inet_addr(hostNameOwned);
if (m_dns && ip == INADDR_NONE && !block)
{
const int r=m_dns->resolve(hostNameOwned,&ip);
if (r<0) break; // error!

if (r>0) ip = INADDR_NONE;
}
#ifndef EEL_NET_NO_SYNC_DNS
else if (ip == INADDR_NONE)
{
struct hostent *he = gethostbyname(hostNameOwned);
if (he) ip = *(int *)he->h_addr;
}
#endif
if (hostNameOwned || ip != INADDR_NONE)
{
if (ip != INADDR_NONE)
{
free(hostNameOwned);
hostNameOwned=NULL;
}

s->state = STATE_RESOLVING;
s->hostname = hostNameOwned;
s->blockmode = !!block;
s->port = port;
if (hostNameOwned || __run_connect(s,ip)) return x + CONNECTION_ID_BASE;

s->state=STATE_FREE;
s->hostname=NULL;
}
break;
}
}
free(hostNameOwned);
return -1;
}

EEL_F eel_net_state::onListen(void *opaque, EEL_F handle, int mode, EEL_F *ifStr, EEL_F *ipOut)
{
const int port = (int) handle;
if (port < 1 || port > 65535)
{
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("tcp_listen(%d): invalid port specified, will never succeed",port);
#endif
return 0.0;
}
#ifdef _WIN32
if (!m_had_socketlib_init)
{
m_had_socketlib_init=1;
WSADATA wsaData;
WSAStartup(MAKEWORD(1, 1), &wsaData);
}
#endif
SOCKET *sockptr = m_listens.GetPtr(port);
if (mode<0)
{
if (!sockptr) return -1.0;

SOCKET ss=*sockptr;
m_listens.Delete(port);
if (ss != INVALID_SOCKET)
{
shutdown(ss, SHUT_RDWR);
closesocket(ss);
}
return 0.0;
}
if (!sockptr)
{
struct sockaddr_in sin;
memset((char *) &sin, 0,sizeof(sin));
if (ifStr)
{
EEL_STRING_MUTEXLOCK_SCOPE
const char *fn = EEL_STRING_GET_FOR_INDEX(*ifStr,NULL);
#ifdef EEL_STRING_DEBUGOUT
if (!fn) EEL_STRING_DEBUGOUT("tcp_listen(%d): bad string identifier %f for second parameter (interface)",port,*ifStr);
#endif
if (fn && *fn) sin.sin_addr.s_addr=inet_addr(fn);
}
if (!sin.sin_addr.s_addr || sin.sin_addr.s_addr==INADDR_NONE) sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_family = AF_INET;
sin.sin_port = htons( (short) port );
SOCKET sock = socket(AF_INET,SOCK_STREAM,0);
if (sock != INVALID_SOCKET)
{
SET_SOCK_DEFAULTS(sock);
SET_SOCK_BLOCK(sock,0);
if (bind(sock,(struct sockaddr *)&sin,sizeof(sin)) || listen(sock,8)==-1)
{
shutdown(sock, SHUT_RDWR);
closesocket(sock);
sock=INVALID_SOCKET;
}
}
#ifdef EEL_STRING_DEBUGOUT
//if (sock == INVALID_SOCKET) EEL_STRING_DEBUGOUT("tcp_listen(%d): failed listening on port",port);
// we report -1 to the caller, no need to error message
#endif
m_listens.Insert(port,sock);
sockptr = m_listens.GetPtr(port);
}
if (!sockptr || *sockptr == INVALID_SOCKET) return -1;

struct sockaddr_in saddr;
socklen_t length = sizeof(struct sockaddr_in);
SOCKET newsock = accept(*sockptr, (struct sockaddr *) &saddr, &length);
if (newsock == INVALID_SOCKET)
{
return 0; // nothing to report here
}
SET_SOCK_DEFAULTS(newsock);

int x;
for(x=0;x<m_cons.GetSize();x++)
{
connection_state *cs=m_cons.Get()+x;
if (cs->state == STATE_FREE)
{
cs->state=STATE_CONNECTED;
free(cs->hostname);
cs->hostname=NULL;
cs->sock = newsock;
cs->blockmode=true;
cs->port=0;
if (ipOut)
{
EEL_STRING_MUTEXLOCK_SCOPE
WDL_FastString *ws=NULL;
EEL_STRING_GET_FOR_WRITE(*ipOut,&ws);
if (ws)
{
const unsigned int a = ntohl(saddr.sin_addr.s_addr);
ws->SetFormatted(128,"%d.%d.%d.%d",(a>>24)&0xff,(a>>16)&0xff,(a>>8)&0xff,a&0xff);
}
else
{
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("tcp_listen(%d): bad string identifier %f for third parameter (IP-out)",port,*ipOut);
#endif
}
}
return x + CONNECTION_ID_BASE;
}
}
shutdown(newsock, SHUT_RDWR);
closesocket(newsock);
return -1;


}

int eel_net_state::__run_connect(connection_state *cs, unsigned int ip)
{
SOCKET s=socket(AF_INET,SOCK_STREAM,0);
if (s == INVALID_SOCKET) return 0;
SET_SOCK_DEFAULTS(s);

if (!cs->blockmode) SET_SOCK_BLOCK(s,0);

struct sockaddr_in sa={0,};
sa.sin_family=AF_INET;
sa.sin_addr.s_addr = ip;
sa.sin_port = htons(cs->port);
if (!connect(s,(struct sockaddr *)&sa,16) || (!cs->blockmode && JNL_ERRNO == JNL_EINPROGRESS))
{
cs->state = STATE_CONNECTED;
cs->sock = s;
return 1;
}
shutdown(s, SHUT_RDWR);
closesocket(s);
return 0;
}

int eel_net_state::__run(connection_state *cs)
{
if (cs->sock != INVALID_SOCKET) return 0;

if (!cs->hostname) return -1;

unsigned int ip=INADDR_NONE;
const int r=m_dns ? m_dns->resolve(cs->hostname,&ip) : -1;
if (r>0) return 0;

free(cs->hostname);
cs->hostname=NULL;

if (r<0 || !__run_connect(cs,ip))
{
cs->state = STATE_ERR;
return -1;
}

return 0;
}

int eel_net_state::do_recv(void *opaque, EEL_F h, char *buf, int maxlen)
{
const int idx=(int)h-CONNECTION_ID_BASE;
if (idx>=0 && idx<m_cons.GetSize())
{
connection_state *s=m_cons.Get()+idx;
#ifdef EEL_STRING_DEBUGOUT
if (s->sock == INVALID_SOCKET && !s->hostname)
EEL_STRING_DEBUGOUT("tcp_recv: connection identifier %f is not open",h);
#endif
if (__run(s) || s->sock == INVALID_SOCKET) return s->state == STATE_ERR ? -1 : 0;

if (maxlen == 0) return 0;

const int rv=(int)recv(s->sock,buf,maxlen,0);
if (rv < 0 && !s->blockmode && (JNL_ERRNO == JNL_EWOULDBLOCK || JNL_ERRNO == JNL_ENOTCONN)) return 0;

if (!rv) return -1; // TCP, 0=connection terminated

return rv;
}
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("tcp_recv: connection identifier %f is out of range",h);
#endif
return -1;
}

int eel_net_state::do_send(void *opaque, EEL_F h, const char *src, int len)
{
const int idx=(int)h-CONNECTION_ID_BASE;
if (idx>=0 && idx<m_cons.GetSize())
{
connection_state *s=m_cons.Get()+idx;
#ifdef EEL_STRING_DEBUGOUT
if (s->sock == INVALID_SOCKET && !s->hostname)
EEL_STRING_DEBUGOUT("tcp_send: connection identifier %f is not open",h);
#endif
if (__run(s) || s->sock == INVALID_SOCKET) return s->state == STATE_ERR ? -1 : 0;
const int rv=(int)send(s->sock,src,len,0);
if (rv < 0 && !s->blockmode && (JNL_ERRNO == JNL_EWOULDBLOCK || JNL_ERRNO == JNL_ENOTCONN)) return 0;
return rv;
}
else
{
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("tcp_send: connection identifier %f out of range",h);
#endif
}
return -1;
}

EEL_F eel_net_state::set_block(void *opaque, EEL_F handle, bool block)
{
int idx=(int)handle-CONNECTION_ID_BASE;
if (idx>=0 && idx<m_cons.GetSize())
{
connection_state *s=m_cons.Get()+idx;
if (s->blockmode != block)
{
s->blockmode=block;
if (s->sock != INVALID_SOCKET)
{
SET_SOCK_BLOCK(s->sock,(block?1:0));
}
else
{
#ifdef EEL_STRING_DEBUGOUT
if (!s->hostname) EEL_STRING_DEBUGOUT("tcp_set_block: connection identifier %f is not open",handle);
#endif
}
return 1;
}
}
else
{
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("tcp_set_block: connection identifier %f out of range",handle);
#endif
}
return 0;
}

EEL_F eel_net_state::onClose(void *opaque, EEL_F handle)
{
int idx=(int)handle-CONNECTION_ID_BASE;
if (idx>=0 && idx<m_cons.GetSize())
{
connection_state *s=m_cons.Get()+idx;
const bool hadhn = !!s->hostname;
free(s->hostname);
s->hostname = NULL;
s->state = STATE_ERR;
if (s->sock != INVALID_SOCKET)
{
shutdown(s->sock,SHUT_RDWR);
closesocket(s->sock);
s->sock = INVALID_SOCKET;
return 1.0;
}
else if (!hadhn)
{
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("tcp_close: connection identifier %f is not open",handle);
#endif
}
}
else
{
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("tcp_close: connection identifier %f is out of range",handle);
#endif
}
return 0.0;
}


static EEL_F NSEEL_CGEN_CALL _eel_tcp_connect(void *opaque, INT_PTR np, EEL_F **parms)
{
eel_net_state *ctx;
if (np > 1 && NULL != (ctx=EEL_NET_GET_CONTEXT(opaque)))
{
char *dest=NULL;
{
EEL_STRING_MUTEXLOCK_SCOPE
const char *fn = EEL_STRING_GET_FOR_INDEX(parms[0][0],NULL);
if (fn) dest=strdup(fn);
else
{
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("tcp_connect(): host string identifier %f invalid",parms[0][0]);
#endif
}
}
if (dest) return ctx->onConnect(dest, (int) (parms[1][0]+0.5), np < 3 || parms[2][0] >= 0.5);
}
return -1.0;
}

static EEL_F NSEEL_CGEN_CALL _eel_tcp_set_block(void *opaque, EEL_F *handle, EEL_F *bl)
{
eel_net_state *ctx;
if (NULL != (ctx=EEL_NET_GET_CONTEXT(opaque))) return ctx->set_block(opaque,*handle, *bl >= 0.5);
return 0;
}

static EEL_F NSEEL_CGEN_CALL _eel_tcp_close(void *opaque, EEL_F *handle)
{
eel_net_state *ctx;
if (NULL != (ctx=EEL_NET_GET_CONTEXT(opaque))) return ctx->onClose(opaque,*handle);
return 0;
}

static EEL_F NSEEL_CGEN_CALL _eel_tcp_recv(void *opaque, INT_PTR np, EEL_F **parms)
{
eel_net_state *ctx;
if (np > 1 && NULL != (ctx=EEL_NET_GET_CONTEXT(opaque)))
{
char buf[EEL_STRING_MAXUSERSTRING_LENGTH_HINT];
int ml = np > 2 ? (int)parms[2][0] : 4096;
if (ml < 0 || ml > EEL_STRING_MAXUSERSTRING_LENGTH_HINT) ml = EEL_STRING_MAXUSERSTRING_LENGTH_HINT;

ml=ctx->do_recv(opaque,parms[0][0],buf,ml);

{
EEL_STRING_MUTEXLOCK_SCOPE
WDL_FastString *ws=NULL;
EEL_STRING_GET_FOR_WRITE(parms[1][0],&ws);
if (ws)
{
if (ml<=0) ws->Set("");
else ws->SetRaw(buf,ml);
}
}
return ml;
}
return -1;
}

static EEL_F NSEEL_CGEN_CALL _eel_tcp_send(void *opaque, INT_PTR np, EEL_F **parms)
{
eel_net_state *ctx;
if (np > 1 && NULL != (ctx=EEL_NET_GET_CONTEXT(opaque)))
{
char buf[EEL_NET_MAXSEND];

int l;
{
EEL_STRING_MUTEXLOCK_SCOPE
WDL_FastString *ws=NULL;
const char *fn = EEL_STRING_GET_FOR_INDEX(parms[1][0],&ws);
l = ws ? ws->GetLength() : (int) strlen(fn);
if (np > 2)
{
int al=(int)parms[2][0];
if (al<0) al=0;
if (al<l) l=al;
}
if (l > 0) memcpy(buf,fn,l);
}
if (l>0) return ctx->do_send(opaque,parms[0][0],buf,l);
return 0;
}
return -1;
}

static EEL_F NSEEL_CGEN_CALL _eel_tcp_listen(void *opaque, INT_PTR np, EEL_F **parms)
{
eel_net_state *ctx;
if (NULL != (ctx=EEL_NET_GET_CONTEXT(opaque))) return ctx->onListen(opaque,parms[0][0],1,np>1?parms[1]:NULL,np>2?parms[2]:NULL);
return 0;
}

static EEL_F NSEEL_CGEN_CALL _eel_tcp_listen_end(void *opaque, EEL_F *handle)
{
eel_net_state *ctx;
if (NULL != (ctx=EEL_NET_GET_CONTEXT(opaque))) return ctx->onListen(opaque,*handle,-1,NULL,NULL);
return 0;
}


void EEL_tcp_register()
{
NSEEL_addfunc_varparm("tcp_listen",1,NSEEL_PProc_THIS,&_eel_tcp_listen);
NSEEL_addfunc_retval("tcp_listen_end",1,NSEEL_PProc_THIS,&_eel_tcp_listen_end);

NSEEL_addfunc_varparm("tcp_connect",2,NSEEL_PProc_THIS,&_eel_tcp_connect);
NSEEL_addfunc_varparm("tcp_send",2,NSEEL_PProc_THIS,&_eel_tcp_send);
NSEEL_addfunc_varparm("tcp_recv",2,NSEEL_PProc_THIS,&_eel_tcp_recv);

NSEEL_addfunc_retval("tcp_set_block",2,NSEEL_PProc_THIS,&_eel_tcp_set_block);
NSEEL_addfunc_retval("tcp_close",1,NSEEL_PProc_THIS,&_eel_tcp_close);
}

#ifdef EEL_WANT_DOCUMENTATION
const char *eel_net_function_reference =
"tcp_listen\tport[,\"interface\",#ip_out]\tListens on port specified. Returns less than 0 if could not listen, 0 if no new connection available, or greater than 0 (as a TCP connection ID) if a new connection was made. If a connection made and #ip_out specified, it will be set to the remote IP. interface can be empty for all interfaces, otherwise an interface IP as a string.\0"
"tcp_listen_end\tport\tEnds listening on port specified.\0"
"tcp_connect\t\"address\",port[,block]\tCreate a new TCP connection to address:port. If block is specified and 0, connection will be made nonblocking. Returns TCP connection ID greater than 0 on success.\0"
"tcp_send\tconnection,\"str\"[,len]\tSends a string to connection. Returns -1 on error, 0 if connection is non-blocking and would block, otherwise returns length sent. If len is specified and not less than 1, only the first len bytes of the string parameter will be sent.\0"
"tcp_recv\tconnection,#str[,maxlen]\tReceives data from a connection to #str. If maxlen is specified, no more than maxlen bytes will be received. If non-blocking, 0 will be returned if would block. Returns less than 0 if error.\0"
"tcp_set_block\tconnection,block\tSets whether a connection blocks.\0"
"tcp_close\tconnection\tCloses a TCP connection created by tcp_listen() or tcp_connect().\0"
;
#endif

#endif

+ 1668
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_strings.h
File diff suppressed because it is too large
View File


+ 776
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eelscript.h View File

@@ -0,0 +1,776 @@
#ifndef _WIN32
#include <unistd.h>
#ifndef EELSCRIPT_NO_LICE
#include "../swell/swell.h"
#endif
#endif

#include "../wdltypes.h"
#include "../ptrlist.h"
#include "../wdlstring.h"
#include "../assocarray.h"
#include "../queue.h"
#include "../win32_utf8.h"
#include "ns-eel.h"


#ifndef EELSCRIPT_MAX_FILE_HANDLES
#define EELSCRIPT_MAX_FILE_HANDLES 512
#endif
#ifndef EELSCRIPT_FILE_HANDLE_INDEX_BASE
#define EELSCRIPT_FILE_HANDLE_INDEX_BASE 1000000
#endif
#ifndef EEL_STRING_MAXUSERSTRING_LENGTH_HINT
#define EEL_STRING_MAXUSERSTRING_LENGTH_HINT (1<<16) // 64KB per string max
#endif
#ifndef EEL_STRING_MAX_USER_STRINGS
#define EEL_STRING_MAX_USER_STRINGS 32768
#endif
#ifndef EEL_STRING_LITERAL_BASE
#define EEL_STRING_LITERAL_BASE 2000000
#endif
#ifndef EELSCRIPT_LICE_MAX_IMAGES
#define EELSCRIPT_LICE_MAX_IMAGES 1024
#endif

#ifndef EELSCRIPT_LICE_MAX_FONTS
#define EELSCRIPT_LICE_MAX_FONTS 128
#endif

#ifndef EELSCRIPT_NET_MAXCON
#define EELSCRIPT_NET_MAXCON 4096
#endif

#ifndef EELSCRIPT_LICE_CLASSNAME
#define EELSCRIPT_LICE_CLASSNAME "eelscript_gfx"
#endif


// #define EELSCRIPT_NO_NET
// #define EELSCRIPT_NO_LICE
// #define EELSCRIPT_NO_FILE
// #define EELSCRIPT_NO_FFT
// #define EELSCRIPT_NO_MDCT
// #define EELSCRIPT_NO_EVAL

class eel_string_context_state;
#ifndef EELSCRIPT_NO_NET
class eel_net_state;
#endif
#ifndef EELSCRIPT_NO_LICE
class eel_lice_state;
#endif

class eelScriptInst {
public:

static int init();

eelScriptInst();
virtual ~eelScriptInst();

NSEEL_CODEHANDLE compile_code(const char *code, const char **err);
int runcode(const char *code, int showerr, const char *showerrfn, bool canfree, bool ignoreEndOfInputChk, bool doExec);
int loadfile(const char *fn, const char *callerfn, bool allowstdin);

NSEEL_VMCTX m_vm;

WDL_PtrList<void> m_code_freelist;

#ifndef EELSCRIPT_NO_FILE
FILE *m_handles[EELSCRIPT_MAX_FILE_HANDLES];
virtual EEL_F OpenFile(const char *fn, const char *mode)
{
if (!*fn || !*mode) return 0.0;
#ifndef EELSCRIPT_NO_STDIO
if (!strcmp(fn,"stdin")) return 1;
if (!strcmp(fn,"stdout")) return 2;
if (!strcmp(fn,"stderr")) return 3;
#endif

WDL_FastString fnstr(fn);
if (!translateFilename(&fnstr,mode)) return 0.0;

int x;
for (x=0;x<EELSCRIPT_MAX_FILE_HANDLES && m_handles[x];x++);
if (x>= EELSCRIPT_MAX_FILE_HANDLES) return 0.0;

FILE *fp = fopenUTF8(fnstr.Get(),mode);
if (!fp) return 0.0;
m_handles[x]=fp;
return x + EELSCRIPT_FILE_HANDLE_INDEX_BASE;
}
virtual EEL_F CloseFile(int fp_idx)
{
fp_idx-=EELSCRIPT_FILE_HANDLE_INDEX_BASE;
if (fp_idx>=0 && fp_idx<EELSCRIPT_MAX_FILE_HANDLES && m_handles[fp_idx])
{
fclose(m_handles[fp_idx]);
m_handles[fp_idx]=0;
return 0.0;
}
return -1.0;
}
virtual FILE *GetFileFP(int fp_idx)
{
#ifndef EELSCRIPT_NO_STDIO
if (fp_idx==1) return stdin;
if (fp_idx==2) return stdout;
if (fp_idx==3) return stderr;
#endif
fp_idx-=EELSCRIPT_FILE_HANDLE_INDEX_BASE;
if (fp_idx>=0 && fp_idx<EELSCRIPT_MAX_FILE_HANDLES) return m_handles[fp_idx];
return NULL;
}

#endif

virtual bool translateFilename(WDL_FastString *fs, const char *mode) { return true; }

virtual bool GetFilenameForParameter(EEL_F idx, WDL_FastString *fs, int iswrite);

eel_string_context_state *m_string_context;
#ifndef EELSCRIPT_NO_NET
eel_net_state *m_net_state;
#endif
#ifndef EELSCRIPT_NO_LICE
eel_lice_state *m_gfx_state;
#endif

#ifndef EELSCRIPT_NO_EVAL
struct evalCacheEnt {
char *str;
NSEEL_CODEHANDLE ch;
};
int m_eval_depth;
WDL_TypedBuf<evalCacheEnt> m_eval_cache;
virtual char *evalCacheGet(const char *str, NSEEL_CODEHANDLE *ch);
virtual void evalCacheDispose(char *key, NSEEL_CODEHANDLE ch);
WDL_Queue m_defer_eval, m_atexit_eval;
void runCodeQ(WDL_Queue *q, const char *fname);
void runAtExitCode()
{
runCodeQ(&m_atexit_eval,"atexit");
m_atexit_eval.Clear(); // make sure nothing gets added in atexit(), in case the user called runAtExitCode before destroying
}
#endif
virtual bool run_deferred(); // requires eval support to be useful
virtual bool has_deferred();


WDL_StringKeyedArray<bool> m_loaded_fnlist; // imported file list (to avoid repeats)
};

//#define EEL_STRINGS_MUTABLE_LITERALS
//#define EEL_STRING_WANT_MUTEX


#define EEL_STRING_GET_CONTEXT_POINTER(opaque) (((eelScriptInst *)opaque)->m_string_context)
#ifndef EEL_STRING_STDOUT_WRITE
#ifndef EELSCRIPT_NO_STDIO
#define EEL_STRING_STDOUT_WRITE(x,len) { fwrite(x,len,1,stdout); fflush(stdout); }
#endif
#endif
#include "eel_strings.h"

#include "eel_misc.h"


#ifndef EELSCRIPT_NO_FILE
#define EEL_FILE_OPEN(fn,mode) ((eelScriptInst*)opaque)->OpenFile(fn,mode)
#define EEL_FILE_GETFP(fp) ((eelScriptInst*)opaque)->GetFileFP(fp)
#define EEL_FILE_CLOSE(fpindex) ((eelScriptInst*)opaque)->CloseFile(fpindex)

#include "eel_files.h"
#endif

#ifndef EELSCRIPT_NO_FFT
#include "eel_fft.h"
#endif

#ifndef EELSCRIPT_NO_MDCT
#include "eel_mdct.h"
#endif

#ifndef EELSCRIPT_NO_NET
#define EEL_NET_GET_CONTEXT(opaque) (((eelScriptInst *)opaque)->m_net_state)
#include "eel_net.h"
#endif

#ifndef EELSCRIPT_NO_LICE
#ifndef EEL_LICE_WANT_STANDALONE
#define EEL_LICE_WANT_STANDALONE
#endif
#ifndef EELSCRIPT_LICE_NOUPDATE
#define EEL_LICE_WANT_STANDALONE_UPDATE // gfx_update() which runs message pump and updates screen etc
#endif
#define EEL_LICE_GET_FILENAME_FOR_STRING(idx, fs, p) (((eelScriptInst*)opaque)->GetFilenameForParameter(idx,fs,p))
#define EEL_LICE_GET_CONTEXT(opaque) ((opaque) ? (((eelScriptInst *)opaque)->m_gfx_state) : NULL)
#include "eel_lice.h"
#endif

#ifndef EELSCRIPT_NO_EVAL
#define EEL_EVAL_GET_CACHED(str, ch) ((eelScriptInst *)opaque)->evalCacheGet(str,&(ch))
#define EEL_EVAL_SET_CACHED(str, ch) ((eelScriptInst *)opaque)->evalCacheDispose(str,ch)
#define EEL_EVAL_GET_VMCTX(opaque) (((eelScriptInst *)opaque)->m_vm)
#define EEL_EVAL_SCOPE_ENTER (((eelScriptInst *)opaque)->m_eval_depth < 3 ? \
++((eelScriptInst *)opaque)->m_eval_depth : 0)
#define EEL_EVAL_SCOPE_LEAVE ((eelScriptInst *)opaque)->m_eval_depth--;
#include "eel_eval.h"

static EEL_F NSEEL_CGEN_CALL _eel_defer(void *opaque, EEL_F *s)
{
EEL_STRING_MUTEXLOCK_SCOPE
const char *str=EEL_STRING_GET_FOR_INDEX(*s,NULL);
if (str && *str && *s >= EEL_STRING_MAX_USER_STRINGS) // don't allow defer(0) etc
{
eelScriptInst *inst = (eelScriptInst *)opaque;
if (inst->m_defer_eval.Available() < EEL_STRING_MAXUSERSTRING_LENGTH_HINT)
{
inst->m_defer_eval.Add(str,strlen(str)+1);
return 1.0;
}
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("defer(): too much defer() code already added, ignoring");
#endif
}
#ifdef EEL_STRING_DEBUGOUT
else if (!str)
{
EEL_STRING_DEBUGOUT("defer(): invalid string identifier specified %f",*s);
}
else if (*s < EEL_STRING_MAX_USER_STRINGS)
{
EEL_STRING_DEBUGOUT("defer(): user string identifier %f specified but not allowed",*s);
}
#endif
return 0.0;
}
static EEL_F NSEEL_CGEN_CALL _eel_atexit(void *opaque, EEL_F *s)
{
EEL_STRING_MUTEXLOCK_SCOPE
const char *str=EEL_STRING_GET_FOR_INDEX(*s,NULL);
if (str && *str && *s >= EEL_STRING_MAX_USER_STRINGS) // don't allow atexit(0) etc
{
eelScriptInst *inst = (eelScriptInst *)opaque;
if (inst->m_atexit_eval.Available() < EEL_STRING_MAXUSERSTRING_LENGTH_HINT)
{
inst->m_atexit_eval.Add(str,strlen(str)+1);
return 1.0;
}
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("atexit(): too much atexit() code already added, ignoring");
#endif
}
#ifdef EEL_STRING_DEBUGOUT
else if (!str)
{
EEL_STRING_DEBUGOUT("atexit(): invalid string identifier specified %f",*s);
}
else if (*s < EEL_STRING_MAX_USER_STRINGS)
{
EEL_STRING_DEBUGOUT("atexit(): user string identifier %f specified but not allowed",*s);
}
#endif
return 0.0;
}
#endif


#define opaque ((void *)this)

eelScriptInst::eelScriptInst() : m_loaded_fnlist(false)
{
#ifndef EELSCRIPT_NO_FILE
memset(m_handles,0,sizeof(m_handles));
#endif
m_vm = NSEEL_VM_alloc();
#ifdef EEL_STRING_DEBUGOUT
if (!m_vm) EEL_STRING_DEBUGOUT("NSEEL_VM_alloc(): failed");
#endif
NSEEL_VM_SetCustomFuncThis(m_vm,this);
#ifdef NSEEL_ADDFUNC_DESTINATION
NSEEL_VM_SetFunctionTable(m_vm,NSEEL_ADDFUNC_DESTINATION);
#endif

m_string_context = new eel_string_context_state;
eel_string_initvm(m_vm);
#ifndef EELSCRIPT_NO_NET
m_net_state = new eel_net_state(EELSCRIPT_NET_MAXCON,NULL);
#endif
#ifndef EELSCRIPT_NO_LICE
m_gfx_state = new eel_lice_state(m_vm,this,EELSCRIPT_LICE_MAX_IMAGES,EELSCRIPT_LICE_MAX_FONTS);

m_gfx_state->resetVarsToStock();
#endif
#ifndef EELSCRIPT_NO_EVAL
m_eval_depth=0;
#endif
}

eelScriptInst::~eelScriptInst()
{
#ifndef EELSCRIPT_NO_EVAL
if (m_atexit_eval.GetSize()>0) runAtExitCode();
#endif
int x;
m_code_freelist.Empty((void (*)(void *))NSEEL_code_free);
#ifndef EELSCRIPT_NO_EVAL
for (x=0;x<m_eval_cache.GetSize();x++)
{
free(m_eval_cache.Get()[x].str);
NSEEL_code_free(m_eval_cache.Get()[x].ch);
}
#endif

if (m_vm) NSEEL_VM_free(m_vm);

#ifndef EELSCRIPT_NO_FILE
for (x=0;x<EELSCRIPT_MAX_FILE_HANDLES;x++)
{
if (m_handles[x]) fclose(m_handles[x]);
m_handles[x]=0;
}
#endif
delete m_string_context;
#ifndef EELSCRIPT_NO_NET
delete m_net_state;
#endif
#ifndef EELSCRIPT_NO_LICE
delete m_gfx_state;
#endif
}

bool eelScriptInst::GetFilenameForParameter(EEL_F idx, WDL_FastString *fs, int iswrite)
{
const char *fmt = EEL_STRING_GET_FOR_INDEX(idx,NULL);
if (!fmt) return false;
fs->Set(fmt);
return translateFilename(fs,iswrite?"w":"r");
}

NSEEL_CODEHANDLE eelScriptInst::compile_code(const char *code, const char **err)
{
if (!m_vm)
{
*err = "EEL VM not initialized";
return NULL;
}
NSEEL_CODEHANDLE ch = NSEEL_code_compile_ex(m_vm, code, 0, NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS);
if (ch)
{
m_string_context->update_named_vars(m_vm);
m_code_freelist.Add((void*)ch);
return ch;
}
*err = NSEEL_code_getcodeerror(m_vm);
return NULL;
}

int eelScriptInst::runcode(const char *codeptr, int showerr, const char *showerrfn, bool canfree, bool ignoreEndOfInputChk, bool doExec)
{
if (m_vm)
{
NSEEL_CODEHANDLE code = NSEEL_code_compile_ex(m_vm,codeptr,0,canfree ? 0 : NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS);
if (code) m_string_context->update_named_vars(m_vm);

char *err;
if (!code && (err=NSEEL_code_getcodeerror(m_vm)))
{
if (!ignoreEndOfInputChk && (NSEEL_code_geterror_flag(m_vm)&1)) return 1;
if (showerr)
{
#ifdef EEL_STRING_DEBUGOUT
if (showerr==2)
{
EEL_STRING_DEBUGOUT("Warning: %s:%s",WDL_get_filepart(showerrfn),err);
}
else
{
EEL_STRING_DEBUGOUT("%s:%s",WDL_get_filepart(showerrfn),err);
}
#endif
}
return -1;
}
else
{
if (code)
{
#ifdef EELSCRIPT_DO_DISASSEMBLE
codeHandleType *p = (codeHandleType*)code;

char buf[512];
buf[0]=0;
#ifdef _WIN32
GetTempPath(sizeof(buf)-64,buf);
lstrcatn(buf,"jsfx-out",sizeof(buf));
#else
lstrcpyn_safe(buf,"/tmp/jsfx-out",sizeof(buf));
#endif
FILE *fp = fopenUTF8(buf,"wb");
if (fp)
{
fwrite(p->code,1,p->code_size,fp);
fclose(fp);
char buf2[2048];
#ifdef _WIN32
snprintf(buf2,sizeof(buf2),"disasm \"%s\"",buf);
#else
#ifdef __aarch64__
snprintf(buf2,sizeof(buf2), "objdump -D -b binary -maarch64 \"%s\"",buf);
#elif defined(__arm__)
snprintf(buf2,sizeof(buf2), "objdump -D -b binary -m arm \"%s\"",buf);
#elif defined(__LP64__)
#ifdef __APPLE__
snprintf(buf2,sizeof(buf2),"distorm3 --b64 \"%s\"",buf);
#else
snprintf(buf2,sizeof(buf2),"objdump -D -b binary -m i386:x86-64 \"%s\"",buf);
#endif
#else
snprintf(buf2,sizeof(buf2),"distorm3 --b32 \"%s\"",buf);
#endif
#endif
system(buf2);
}
#endif

if (doExec) NSEEL_code_execute(code);
if (canfree) NSEEL_code_free(code);
else m_code_freelist.Add((void*)code);
}
return 0;
}
}
return -1;
}


FILE *eelscript_resolvePath(WDL_FastString &usefn, const char *fn, const char *callerfn)
{
// resolve path relative to current
int x;
bool had_abs=false;
for (x=0;x<2; x ++)
{
#ifdef _WIN32
if (!x && ((fn[0] == '\\' && fn[1] == '\\') || (fn[0] && fn[1] == ':')))
#else
if (!x && fn[0] == '/')
#endif
{
usefn.Set(fn);
had_abs=true;
}
else
{
const char *fnu = fn;
if (x)
{
while (*fnu) fnu++;
while (fnu >= fn && *fnu != '\\' && *fnu != '/') fnu--;
if (fnu < fn) break;
fnu++;
}

usefn.Set(callerfn);
int l=usefn.GetLength();
while (l > 0 && usefn.Get()[l-1] != '\\' && usefn.Get()[l-1] != '/') l--;
if (l > 0)
{
usefn.SetLen(l);
usefn.Append(fnu);
}
else
{
usefn.Set(fnu);
}
int last_slash_pos=-1;
for (l = 0; l < usefn.GetLength(); l ++)
{
if (usefn.Get()[l] == '/' || usefn.Get()[l] == '\\')
{
if (usefn.Get()[l+1] == '.' && usefn.Get()[l+2] == '.' &&
(usefn.Get()[l+3] == '/' || usefn.Get()[l+3] == '\\'))
{
if (last_slash_pos >= 0)
usefn.DeleteSub(last_slash_pos, l+3-last_slash_pos);
else
usefn.DeleteSub(0,l+3+1);
}
else
{
last_slash_pos=l;
}
}
// take currentfn, remove filename part, add fnu
}
}

FILE *fp = fopenUTF8(usefn.Get(),"r");
if (fp) return fp;
}
if (had_abs) usefn.Set(fn);
return NULL;
}

int eelScriptInst::loadfile(const char *fn, const char *callerfn, bool allowstdin)
{
WDL_FastString usefn;
FILE *fp = NULL;
if (!strcmp(fn,"-"))
{
if (callerfn)
{
#ifdef EEL_STRING_DEBUGOUT
EEL_STRING_DEBUGOUT("@import: can't import \"-\" (stdin)");
#endif
return -1;
}
if (allowstdin)
{
fp = stdin;
fn = "(stdin)";
}
}
else if (!callerfn)
{
fp = fopenUTF8(fn,"r");
if (fp) m_loaded_fnlist.Insert(fn,true);
}
else
{
fp = eelscript_resolvePath(usefn,fn,callerfn);
if (fp)
{
if (m_loaded_fnlist.Get(usefn.Get()))
{
fclose(fp);
return 0;
}
m_loaded_fnlist.Insert(usefn.Get(),true);
fn = usefn.Get();
}
}

if (!fp)
{
#ifdef EEL_STRING_DEBUGOUT
if (callerfn)
EEL_STRING_DEBUGOUT("Warning: @import could not open '%s'",fn);
else
EEL_STRING_DEBUGOUT("Error opening %s",fn);
#endif
return -1;
}

WDL_FastString code;
char line[4096];
for (;;)
{
line[0]=0;
fgets(line,sizeof(line),fp);
if (!line[0]) break;
if (!strnicmp(line,"@import",7) && isspace((unsigned char)line[7]))
{
char *p=line+7;
while (isspace((unsigned char)*p)) p++;

char *ep=p;
while (*ep) ep++;
while (ep>p && isspace((unsigned char)ep[-1])) ep--;
*ep=0;

if (*p) loadfile(p,fn,false);
}
else
{
code.Append(line);
}
}
if (fp != stdin) fclose(fp);

return runcode(code.Get(),callerfn ? 2 : 1, fn,false,true,!callerfn);
}

char *eelScriptInst::evalCacheGet(const char *str, NSEEL_CODEHANDLE *ch)
{
// should mutex protect if multiple threads access this eelScriptInst context
int x=m_eval_cache.GetSize();
while (--x >= 0)
{
char *ret;
if (!strcmp(ret=m_eval_cache.Get()[x].str, str))
{
*ch = m_eval_cache.Get()[x].ch;
m_eval_cache.Delete(x);
return ret;
}
}
return NULL;
}

void eelScriptInst::evalCacheDispose(char *key, NSEEL_CODEHANDLE ch)
{
// should mutex protect if multiple threads access this eelScriptInst context
evalCacheEnt ecc;
ecc.str= key;
ecc.ch = ch;
if (m_eval_cache.GetSize() > 1024)
{
NSEEL_code_free(m_eval_cache.Get()->ch);
free(m_eval_cache.Get()->str);
m_eval_cache.Delete(0);
}
m_eval_cache.Add(ecc);
}

int eelScriptInst::init()
{
EEL_string_register();
#ifndef EELSCRIPT_NO_FILE
EEL_file_register();
#endif
#ifndef EELSCRIPT_NO_FFT
EEL_fft_register();
#endif
#ifndef EELSCRIPT_NO_MDCT
EEL_mdct_register();
#endif
EEL_misc_register();
#ifndef EELSCRIPT_NO_EVAL
EEL_eval_register();
NSEEL_addfunc_retval("defer",1,NSEEL_PProc_THIS,&_eel_defer);
NSEEL_addfunc_retval("runloop", 1, NSEEL_PProc_THIS, &_eel_defer);
NSEEL_addfunc_retval("atexit",1,NSEEL_PProc_THIS,&_eel_atexit);
#endif
#ifndef EELSCRIPT_NO_NET
EEL_tcp_register();
#endif
#ifndef EELSCRIPT_NO_LICE
eel_lice_register();
#ifdef _WIN32
eel_lice_register_standalone(GetModuleHandle(NULL),EELSCRIPT_LICE_CLASSNAME,NULL,NULL);
#else
eel_lice_register_standalone(NULL,EELSCRIPT_LICE_CLASSNAME,NULL,NULL);
#endif
#endif
return 0;
}

bool eelScriptInst::has_deferred()
{
#ifndef EELSCRIPT_NO_EVAL
return m_defer_eval.Available() && m_vm;
#else
return false;
#endif
}

#ifndef EELSCRIPT_NO_EVAL
void eelScriptInst::runCodeQ(WDL_Queue *q, const char *callername)
{
const int endptr = q->Available();
int offs = 0;
while (offs < endptr)
{
if (q->Available() < endptr) break; // should never happen, but safety first!

const char *ptr = (const char *)q->Get() + offs;
offs += strlen(ptr)+1;

NSEEL_CODEHANDLE ch=NULL;
char *sv=evalCacheGet(ptr,&ch);

if (!sv) sv=strdup(ptr);
if (!ch) ch=NSEEL_code_compile(m_vm,sv,0);
if (!ch)
{
free(sv);
#ifdef EEL_STRING_DEBUGOUT
const char *err = NSEEL_code_getcodeerror(m_vm);
if (err) EEL_STRING_DEBUGOUT("%s: error in code: %s",callername,err);
#endif
}
else
{
NSEEL_code_execute(ch);
evalCacheDispose(sv,ch);
}
}
q->Advance(endptr);
}
#endif

bool eelScriptInst::run_deferred()
{
#ifndef EELSCRIPT_NO_EVAL
if (!m_defer_eval.Available()||!m_vm) return false;

runCodeQ(&m_defer_eval,"defer");
m_defer_eval.Compact();
return m_defer_eval.Available()>0;
#else
return false;
#endif
}

#ifdef EEL_WANT_DOCUMENTATION
#include "ns-eel-func-ref.h"

void EELScript_GenerateFunctionList(WDL_PtrList<const char> *fs)
{
const char *p = nseel_builtin_function_reference;
while (*p) { fs->Add(p); p += strlen(p) + 1; }
p = eel_strings_function_reference;
while (*p) { fs->Add(p); p += strlen(p) + 1; }
p = eel_misc_function_reference;
while (*p) { fs->Add(p); p += strlen(p) + 1; }
#ifndef EELSCRIPT_NO_EVAL
fs->Add("atexit\t\"code\"\t"
#ifndef EELSCRIPT_HELP_NO_DEFER_DESC
"Adds code to be executed when the script finishes."
#endif
);
fs->Add("defer\t\"code\"\t"
#ifndef EELSCRIPT_HELP_NO_DEFER_DESC
"Adds code which will be executed some small amount of time after the current code finishes. Identical to runloop()"
#endif
);
fs->Add("runloop\t\"code\"\t"
#ifndef EELSCRIPT_HELP_NO_DEFER_DESC
"Adds code which will be executed some small amount of time after the current code finishes. Identical to defer()"
#endif
);

p = eel_eval_function_reference;
while (*p) { fs->Add(p); p += strlen(p) + 1; }
#endif
#ifndef EELSCRIPT_NO_NET
p = eel_net_function_reference;
while (*p) { fs->Add(p); p += strlen(p) + 1; }
#endif
#ifndef EELSCRIPT_NO_FFT
p = eel_fft_function_reference;
while (*p) { fs->Add(p); p += strlen(p) + 1; }
#endif
#ifndef EELSCRIPT_NO_FILE
p = eel_file_function_reference;
while (*p) { fs->Add(p); p += strlen(p) + 1; }
#endif
#ifndef EELSCRIPT_NO_MDCT
p = eel_mdct_function_reference;
while (*p) { fs->Add(p); p += strlen(p) + 1; }
#endif
#ifndef EELSCRIPT_NO_LICE
p = eel_lice_function_reference;
while (*p) { fs->Add(p); p += strlen(p) + 1; }
#endif

}


#endif

#undef opaque

+ 399
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_aarch64.h View File

@@ -0,0 +1,399 @@
#ifndef _NSEEL_GLUE_AARCH64_H_
#define _NSEEL_GLUE_AARCH64_H_

// x0=return value, first parm, x1-x2 parms (x3-x7 more params)
// x8 return struct?
// x9-x15 temporary
// x16-x17 = PLT, linker
// x18 reserved (TLS)
// x19-x28 callee-saved
// x19 = worktable
// x20 = ramtable
// x21 = consttab
// x22 = worktable ptr
// x23-x28 spare
// x29 frame pointer
// x30 link register
// x31 SP/zero

// x0=p1
// x1=p2
// x2=p3

// d0 is return value for fp?
// d/v/f0-7 = arguments/results
// 8-15 callee saved
// 16-31 temporary

// v8-v15 spill registers
#define GLUE_MAX_SPILL_REGS 8
#define GLUE_SAVE_TO_SPILL_SIZE(x) (4)
#define GLUE_RESTORE_SPILL_TO_FPREG2_SIZE(x) (4)

static void GLUE_RESTORE_SPILL_TO_FPREG2(void *b, int ws)
{
*(unsigned int *)b = 0x1e604101 + (ws<<5); // fmov d1, d8+ws
}
static void GLUE_SAVE_TO_SPILL(void *b, int ws)
{
*(unsigned int *)b = 0x1e604008 + ws; // fmov d8+ws, d0
}


#define GLUE_HAS_FPREG2 1

static const unsigned int GLUE_COPY_FPSTACK_TO_FPREG2[] = { 0x1e604001 }; // fmov d1, d0
static unsigned int GLUE_POP_STACK_TO_FPREG2[] = {
0xfc4107e1 // ldr d1, [sp], #16
};

#define GLUE_MAX_FPSTACK_SIZE 0 // no stack support
#define GLUE_MAX_JMPSIZE ((1<<20) - 1024) // maximum relative jump size

// endOfInstruction is end of jump with relative offset, offset passed in is offset from end of dest instruction.
// 0 = current instruction
static void GLUE_JMP_SET_OFFSET(void *endOfInstruction, int offset)
{
unsigned int *a = (unsigned int*) endOfInstruction - 1;
offset += 4;
offset >>= 2; // as dwords
if ((a[0] & 0xFC000000) == 0x14000000)
{
// NC b = 0x14 + 26 bit offset
a[0] = 0x14000000 | (offset & 0x3FFFFFF);
}
else if ((a[0] & 0xFF000000) == 0x54000000)
{
// condb = 0x54 + 20 bit offset + 5 bit condition: 0=eq, 1=ne, b=lt, c=gt, d=le, a=ge
a[0] = 0x54000000 | (a[0] & 0xF) | ((offset & 0x7FFFF) << 5);
}
}

static const unsigned int GLUE_JMP_NC[] = { 0x14000000 };

static const unsigned int GLUE_JMP_IF_P1_Z[]=
{
0x7100001f, // cmp w0, #0
0x54000000, // b.eq
};
static const unsigned int GLUE_JMP_IF_P1_NZ[]=
{
0x7100001f, // cmp w0, #0
0x54000001, // b.ne
};

#define GLUE_MOV_PX_DIRECTVALUE_TOFPREG2_SIZE 16 // wr=-2, sets d1
#define GLUE_MOV_PX_DIRECTVALUE_SIZE 12
static void GLUE_MOV_PX_DIRECTVALUE_GEN(void *b, INT_PTR v, int wv)
{
static const unsigned int tab[3] = {
0xd2800000, // mov x0, #0000 (val<<5) | reg
0xf2a00000, // movk x0, #0000, lsl 16 (val<<5) | reg
0xf2c00000, // movk x0, #0000, lsl 32 (val<<5) | reg
};
// 0xABAAA, B is register, A are bits of word
unsigned int *p=(unsigned int *)b;
int wvo = wv;
if (wv<0) wv=0;
p[0] = tab[0] | wv | ((v&0xFFFF)<<5);
p[1] = tab[1] | wv | (((v>>16)&0xFFFF)<<5);
p[2] = tab[2] | wv | (((v>>32)&0xFFFF)<<5);
if (wvo == -2) p[3] = 0xfd400001; // ldr d1, [x0]
}

const static unsigned int GLUE_FUNC_ENTER[2] = { 0xa9bf7bfd, 0x910003fd }; // stp x29, x30, [sp, #-16]! ; mov x29, sp
#define GLUE_FUNC_ENTER_SIZE 4
const static unsigned int GLUE_FUNC_LEAVE[1] = { 0 }; // let GLUE_RET pop
#define GLUE_FUNC_LEAVE_SIZE 0
const static unsigned int GLUE_RET[]={ 0xa8c17bfd, 0xd65f03c0 }; // ldp x29,x30, [sp], #16 ; ret

static int GLUE_RESET_WTP(unsigned char *out, void *ptr)
{
const static unsigned int GLUE_SET_WTP_FROM_R19 = 0xaa1303f6; // mov r22, r19
if (out) memcpy(out,&GLUE_SET_WTP_FROM_R19,sizeof(GLUE_SET_WTP_FROM_R19));
return 4;
}


const static unsigned int GLUE_PUSH_P1[1]={ 0xf81f0fe0 }; // str x0, [sp, #-16]!

#define GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(offs) ((offs)>=32768 ? 8 : 4)
static void GLUE_STORE_P1_TO_STACK_AT_OFFS(void *b, int offs)
{
if (offs >= 32768)
{
// add x1, sp, (offs/4096) lsl 12
*(unsigned int *)b = 0x914003e1 + ((offs>>12)<<10);

// str x0, [x1, #offs & 4095]
offs &= 4095;
offs <<= 10-3;
offs &= 0x7FFC00;
((unsigned int *)b)[1] = 0xf9000020 + offs;
}
else
{
// str x0, [sp, #offs]
offs <<= 10-3;
offs &= 0x7FFC00;
*(unsigned int *)b = 0xf90003e0 + offs;
}
}

#define GLUE_MOVE_PX_STACKPTR_SIZE 4
static void GLUE_MOVE_PX_STACKPTR_GEN(void *b, int wv)
{
// mov xX, sp
*(unsigned int *)b = 0x910003e0 + wv;
}

#define GLUE_MOVE_STACK_SIZE 4
static void GLUE_MOVE_STACK(void *b, int amt)
{
if (amt>=0)
{
if (amt >= 4096)
*(unsigned int*)b = 0x914003ff | (((amt+4095)>>12)<<10);
else
*(unsigned int*)b = 0x910003ff | (amt << 10);
}
else
{
amt = -amt;
if (amt >= 4096)
*(unsigned int*)b = 0xd14003ff | (((amt+4095)>>12)<<10);
else
*(unsigned int*)b = 0xd10003ff | (amt << 10);
}
}

#define GLUE_POP_PX_SIZE 4
static void GLUE_POP_PX(void *b, int wv)
{
((unsigned int *)b)[0] = 0xf84107e0 | wv; // ldr x, [sp], 16
}

#define GLUE_SET_PX_FROM_P1_SIZE 4
static void GLUE_SET_PX_FROM_P1(void *b, int wv)
{
*(unsigned int *)b = 0xaa0003e0 | wv;
}


static const unsigned int GLUE_PUSH_P1PTR_AS_VALUE[] =
{
0xfd400007, // ldr d7, [x0]
0xfc1f0fe7, // str d7, [sp, #-16]!
};

static int GLUE_POP_VALUE_TO_ADDR(unsigned char *buf, void *destptr)
{
if (buf)
{
unsigned int *bufptr = (unsigned int *)buf;
*bufptr++ = 0xfc4107e7; // ldr d7, [sp], #16
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0);
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4;
*bufptr++ = 0xfd000007; // str d7, [x0]
}
return 2*4 + GLUE_MOV_PX_DIRECTVALUE_SIZE;
}

static int GLUE_COPY_VALUE_AT_P1_TO_PTR(unsigned char *buf, void *destptr)
{
if (buf)
{
unsigned int *bufptr = (unsigned int *)buf;
*bufptr++ = 0xfd400007; // ldr d7, [x0]
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0);
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4;
*bufptr++ = 0xfd000007; // str d7, [x0]
}
return 2*4 + GLUE_MOV_PX_DIRECTVALUE_SIZE;
}


#ifndef _MSC_VER
#define GLUE_CALL_CODE(bp, cp, rt) do { \
unsigned long f; \
if (!(h->compile_flags&NSEEL_CODE_COMPILE_FLAG_NOFPSTATE) && \
!((f=glue_getscr())&(1<<24))) { \
glue_setscr(f|(1<<24)); \
eel_callcode64(bp, cp, rt); \
glue_setscr(f); \
} else eel_callcode64(bp, cp, rt);\
} while(0)

static void eel_callcode64(INT_PTR bp, INT_PTR cp, INT_PTR rt)
{
//fwrite((void *)cp,4,20,stdout);
//return;
static const double consttab[] = {
NSEEL_CLOSEFACTOR,
0.0,
1.0,
-1.0,
-0.5, // for invsqrt
1.5,
};
__asm__(
"mov x1, %2\n"
"mov x2, %3\n"
"mov x3, %1\n"
"mov x0, %0\n"
"stp x29, x30, [sp, #-64]!\n"
"stp x18, x20, [sp, 16]\n"
"stp x21, x19, [sp, 32]\n"
"stp x22, x23, [sp, 48]\n"
"mov x29, sp\n"
"mov x19, x3\n"
"mov x20, x1\n"
"mov x21, x2\n"
"blr x0\n"
"ldp x29, x30, [sp], 16\n"
"ldp x18, x20, [sp], 16\n"
"ldp x21, x19, [sp], 16\n"
"ldp x22, x23, [sp], 16\n"
::"r" (cp), "r" (bp), "r" (rt), "r" (consttab) :"x0","x1","x2","x3","x4","x5","x6","x7",
"x8","x9","x10","x11","x12","x13","x14","x15",
"v8","v9","v10","v11","v12","v13","v14","v15");
};
#endif

static unsigned char *EEL_GLUE_set_immediate(void *_p, INT_PTR newv)
{
unsigned int *p=(unsigned int *)_p;
// 0xd2800000, // mov x0, #0000 (val<<5) | reg
// 0xf2a00000, // movk x0, #0000, lsl 16 (val<<5) | reg
// 0xf2c00000, // movk x0, #0000, lsl 32 (val<<5) | reg
while (((p[0]>>5)&0xffff)!=0xdead ||
((p[1]>>5)&0xffff)!=0xbeef ||
((p[2]>>5)&0xffff)!=0xbeef) p++;

p[0] = (p[0] & 0xFFE0001F) | ((newv&0xffff)<<5);
p[1] = (p[1] & 0xFFE0001F) | (((newv>>16)&0xffff)<<5);
p[2] = (p[2] & 0xFFE0001F) | (((newv>>32)&0xffff)<<5);

return (unsigned char *)(p+2);
}

#define GLUE_SET_PX_FROM_WTP_SIZE sizeof(int)
static void GLUE_SET_PX_FROM_WTP(void *b, int wv)
{
*(unsigned int *)b = 0xaa1603e0 + wv; // mov x, x22
}

static int GLUE_POP_FPSTACK_TO_PTR(unsigned char *buf, void *destptr)
{
if (buf)
{
unsigned int *bufptr = (unsigned int *)buf;
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0);
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4;

*bufptr++ = 0xfd000000; // str d0, [x0]
}
return GLUE_MOV_PX_DIRECTVALUE_SIZE + sizeof(int);
}

#define GLUE_POP_FPSTACK_SIZE 0
static const unsigned int GLUE_POP_FPSTACK[1] = { 0 }; // no need to pop, not a stack

static const unsigned int GLUE_POP_FPSTACK_TOSTACK[] = {
0xfc1f0fe0, // str d0, [sp, #-16]!

};

static const unsigned int GLUE_POP_FPSTACK_TO_WTP[] = {
0xfc0086c0, // str d0, [x22], #8
};

#define GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE 4
static void GLUE_PUSH_VAL_AT_PX_TO_FPSTACK(void *b, int wv)
{
*(unsigned int *)b = 0xfd400000 + (wv<<5); // ldr d0, [xX]
}

#define GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE (sizeof(GLUE_POP_FPSTACK_TO_WTP) + GLUE_SET_PX_FROM_WTP_SIZE)
static void GLUE_POP_FPSTACK_TO_WTP_TO_PX(unsigned char *buf, int wv)
{
GLUE_SET_PX_FROM_WTP(buf,wv);
memcpy(buf + GLUE_SET_PX_FROM_WTP_SIZE,GLUE_POP_FPSTACK_TO_WTP,sizeof(GLUE_POP_FPSTACK_TO_WTP));
};

static const unsigned int GLUE_SET_P1_Z[] = { 0x52800000 }; // mov w0, #0
static const unsigned int GLUE_SET_P1_NZ[] = { 0x52800020 }; // mov w0, #1


static void *GLUE_realAddress(void *fn, int *size)
{
while ((*(int*)fn & 0xFC000000) == 0x14000000)
{
int offset = (*(int*)fn & 0x3FFFFFF);
if (offset & 0x2000000)
offset |= 0xFC000000;

fn = (int*)fn + offset;
}
static const unsigned int sig[3] = { 0xaa0003e0, 0xaa0103e1, 0xaa0203e2 };
unsigned char *p = (unsigned char *)fn;

while (memcmp(p,sig,sizeof(sig))) p+=4;
p+=sizeof(sig);
fn = p;

while (memcmp(p,sig,sizeof(sig))) p+=4;
*size = p - (unsigned char *)fn;
return fn;
}


static unsigned long __attribute__((unused)) glue_getscr()
{
unsigned long rv;
asm volatile ( "mrs %0, fpcr" : "=r" (rv));
return rv;
}
static void __attribute__((unused)) glue_setscr(unsigned long v)
{
asm volatile ( "msr fpcr, %0" :: "r"(v));
}

void eel_enterfp(int _s[2])
{
unsigned long *s = (unsigned long*)_s;
s[0] = glue_getscr();
glue_setscr(s[0] | (1<<24));
}
void eel_leavefp(int _s[2])
{
unsigned long *s = (unsigned long*)_s;
glue_setscr(s[0]);
}

#define GLUE_HAS_FUSE 1
static int GLUE_FUSE(compileContext *ctx, unsigned char *code, int left_size, int right_size, int fuse_flags, int spill_reg)
{
if (left_size>=4 && right_size == 4)
{
unsigned int instr = ((unsigned int *)code)[-1];
if (spill_reg >= 0 && (instr & 0xfffffc1f) == 0x1e604001) // fmov d1, dX
{
const int src_reg = (instr>>5)&0x1f;
if (src_reg == spill_reg + 8)
{
instr = ((unsigned int *)code)[0];
if ((instr & 0xffffcfff) == 0x1e600820)
{
((unsigned int *)code)[-1] = instr + ((src_reg-1) << 5);
return -4;
}
}
}
}
return 0;
}


#endif

+ 335
- 0
source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_arm.h View File

@@ -0,0 +1,335 @@
#ifndef _NSEEL_GLUE_ARM_H_
#define _NSEEL_GLUE_ARM_H_

// r0=return value, first parm, r1-r2 parms
// r3+ should be reserved
// blx addr
// stmfd sp!, {register list, lr}
// ldmfd sp!, {register list, pc}

// let's make r8 = worktable
// let's make r7 = ramtable
// r6 = consttab
// r5 = worktable ptr

// r0=p1
// r1=p2
// r2=p3

// d0 is return value?


#define GLUE_HAS_FPREG2 1

static const unsigned int GLUE_COPY_FPSTACK_TO_FPREG2[] = {
0xeeb01b40 // fcpyd d1, d0
};

static unsigned int GLUE_POP_STACK_TO_FPREG2[] = {
0xed9d1b00,// vldr d1, [sp]
0xe28dd008,// add sp, sp, #8
};

#define GLUE_MAX_SPILL_REGS 8
#define GLUE_SAVE_TO_SPILL_SIZE(x) (4)
#define GLUE_RESTORE_SPILL_TO_FPREG2_SIZE(x) (4)

static void GLUE_RESTORE_SPILL_TO_FPREG2(void *b, int ws)
{
*(unsigned int *)b = 0xeeb01b48 + ws; // fcpyd d1, d8+ws
}
static void GLUE_SAVE_TO_SPILL(void *b, int ws)
{
*(unsigned int *)b = 0xeeb08b40 + (ws<<12); // fcpyd d8+ws, d0
}


#define GLUE_MAX_FPSTACK_SIZE 0 // no stack support
#define GLUE_MAX_JMPSIZE ((1<<25) - 1024) // maximum relative jump size

// endOfInstruction is end of jump with relative offset, offset passed in is offset from end of dest instruction.
// TODO: verify, but offset probably from next instruction (PC is ahead)
#define GLUE_JMP_SET_OFFSET(endOfInstruction,offset) (((int *)(endOfInstruction))[-1] = (((int *)(endOfInstruction))[-1]&0xFF000000)|((((offset)>>2)-1)))

// /=conditional=always = 0xE
// |/= 101(L), so 8+2+0 = 10 = A
static const unsigned int GLUE_JMP_NC[] = { 0xEA000000 };

static const unsigned int GLUE_JMP_IF_P1_Z[]=
{
0xe1100000, // tst r0, r0
0x0A000000, // branch if Z set
};
static const unsigned int GLUE_JMP_IF_P1_NZ[]=
{
0xe1100000, // tst r0, r0
0x1A000000, // branch if Z clear
};

#define GLUE_MOV_PX_DIRECTVALUE_TOFPREG2_SIZE 12 // wr=-2, sets d1
#define GLUE_MOV_PX_DIRECTVALUE_SIZE 8
static void GLUE_MOV_PX_DIRECTVALUE_GEN(void *b, INT_PTR v, int wv)
{
// requires ARMv6thumb2 or later
const unsigned int reg_add = wdl_max(wv,0) << 12;
static const unsigned int tab[2] = {
0xe3000000, // movw r0, #0000
0xe3400000, // movt r0, #0000
};
// 0xABAAA, B is register, A are bits of word
unsigned int *p=(unsigned int *)b;
p[0] = tab[0] | reg_add | (v&0xfff) | ((v&0xf000)<<4);
p[1] = tab[1] | reg_add | ((v>>16)&0xfff) | ((v&0xf0000000)>>12);
if (wv == -2) p[2] = 0xed901b00; // fldd d1, [r0]
}

const static unsigned int GLUE_FUNC_ENTER[1] = { 0xe92d4010 }; // push {r4, lr}
#define GLUE_FUNC_ENTER_SIZE 4
const static unsigned int GLUE_FUNC_LEAVE[1] = { 0 }; // let GLUE_RET pop
#define GLUE_FUNC_LEAVE_SIZE 0
const static unsigned int GLUE_RET[]={ 0xe8bd8010 }; // pop {r4, pc}

static int GLUE_RESET_WTP(unsigned char *out, void *ptr)
{
const static unsigned int GLUE_SET_WTP_FROM_R8 = 0xe1a05008; // mov r5, r8
if (out) memcpy(out,&GLUE_SET_WTP_FROM_R8,sizeof(GLUE_SET_WTP_FROM_R8));
return sizeof(GLUE_SET_WTP_FROM_R8);
}


const static unsigned int GLUE_PUSH_P1[1]={ 0xe52d0008 }; // push {r0}, aligned to 8


static int arm_encode_constforalu(int amt)
{
int nrot = 16;
while (amt >= 0x100 && nrot > 1)
{
// ARM encodes integers for ALU operations as rotated right by third nibble*2
amt = (amt + 3)>>2;
nrot--;
}
return ((nrot&15) << 8) | amt;
}


#define GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(x) ((x)>=4096 ? 8 : 4)
static void GLUE_STORE_P1_TO_STACK_AT_OFFS(void *b, int offs)
{
if (offs >= 4096)
{
// add r2, sp, (offs&~4095)
*(unsigned int *)b = 0xe28d2000 | arm_encode_constforalu(offs&~4095);
// str r0, [r2, offs&4095]
((unsigned int *)b)[1] = 0xe5820000 + (offs&4095);
}
else
{
// str r0, [sp, #offs]
*(unsigned int *)b = 0xe58d0000 + offs;
}
}

#define GLUE_MOVE_PX_STACKPTR_SIZE 4
static void GLUE_MOVE_PX_STACKPTR_GEN(void *b, int wv)
{
// mov rX, sp
*(unsigned int *)b = 0xe1a0000d + (wv<<12);
}

#define GLUE_MOVE_STACK_SIZE 4
static void GLUE_MOVE_STACK(void *b, int amt)
{
unsigned int instr = 0xe28dd000;
if (amt < 0)
{
instr = 0xe24dd000;
amt=-amt;
}
*(unsigned int*)b = instr | arm_encode_constforalu(amt);
}

#define GLUE_POP_PX_SIZE 4
static void GLUE_POP_PX(void *b, int wv)
{
((unsigned int *)b)[0] = 0xe49d0008 | (wv<<12); // pop {rX}, aligned to 8
}

#define GLUE_SET_PX_FROM_P1_SIZE 4
static void GLUE_SET_PX_FROM_P1(void *b, int wv)
{
*(unsigned int *)b = 0xe1a00000 | (wv<<12); // mov rX, r0
}


static const unsigned int GLUE_PUSH_P1PTR_AS_VALUE[] =
{
0xed907b00, // fldd d7, [r0]
0xe24dd008, // sub sp, sp, #8
0xed8d7b00, // fstd d7, [sp]
};

static int GLUE_POP_VALUE_TO_ADDR(unsigned char *buf, void *destptr)
{
if (buf)
{
unsigned int *bufptr = (unsigned int *)buf;
*bufptr++ = 0xed9d7b00; // fldd d7, [sp]
*bufptr++ = 0xe28dd008; // add sp, sp, #8
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0);
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4;
*bufptr++ = 0xed807b00; // fstd d7, [r0]
}
return 3*4 + GLUE_MOV_PX_DIRECTVALUE_SIZE;
}

static int GLUE_COPY_VALUE_AT_P1_TO_PTR(unsigned char *buf, void *destptr)
{
if (buf)
{
unsigned int *bufptr = (unsigned int *)buf;
*bufptr++ = 0xed907b00; // fldd d7, [r0]
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0);
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4;
*bufptr++ = 0xed807b00; // fstd d7, [r0]
}
return 2*4 + GLUE_MOV_PX_DIRECTVALUE_SIZE;
}


#ifndef _MSC_VER
#define GLUE_CALL_CODE(bp, cp, rt) do { \
unsigned int f; \
if (!(h->compile_flags&NSEEL_CODE_COMPILE_FLAG_NOFPSTATE) && \
!((f=glue_getscr())&(1<<24))) { \
glue_setscr(f|(1<<24)); \
eel_callcode32(bp, cp, rt); \
glue_setscr(f); \
} else eel_callcode32(bp, cp, rt);\
} while(0)

static const double __consttab[] = {
NSEEL_CLOSEFACTOR,
0.0,
1.0,
-1.0,
-0.5, // for invsqrt
1.5,
};

static void eel_callcode32(INT_PTR bp, INT_PTR cp, INT_PTR rt)
{
__asm__ volatile(
"mov r7, %2\n"
"mov r6, %3\n"
"mov r8, %1\n"
"mov r0, %0\n"
"mov r1, sp\n"
"bic sp, sp, #7\n"
"push {r1, lr}\n"
"blx r0\n"
"pop {r1, lr}\n"
"mov sp, r1\n"
::"r" (cp), "r" (bp), "r" (rt), "r" (__consttab) :
"r5", "r6", "r7", "r8", "r10",
"d8","d9","d10","d11","d12","d13","d14","d15");
};
#endif

static unsigned char *EEL_GLUE_set_immediate(void *_p, INT_PTR newv)
{
unsigned int *p=(unsigned int *)_p;
while ((p[0]&0x000F0FFF) != 0x000d0ead &&
(p[1]&0x000F0FFF) != 0x000b0eef) p++;
p[0] = (p[0]&0xFFF0F000) | (newv&0xFFF) | ((newv << 4) & 0xF0000);
p[1] = (p[1]&0xFFF0F000) | ((newv>>16)&0xFFF) | ((newv >> 12)&0xF0000);

return (unsigned char *)(p+1);
}

#define GLUE_SET_PX_FROM_WTP_SIZE sizeof(int)
static void GLUE_SET_PX_FROM_WTP(void *b, int wv)
{
*(unsigned int *)b = 0xe1a00005 + (wv<<12); // mov rX, r5
}

static int GLUE_POP_FPSTACK_TO_PTR(unsigned char *buf, void *destptr)
{
if (buf)
{
unsigned int *bufptr = (unsigned int *)buf;
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0);
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4;

*bufptr++ = 0xed800b00; // fstd d0, [r0]
}
return GLUE_MOV_PX_DIRECTVALUE_SIZE + sizeof(int);
}

#define GLUE_POP_FPSTACK_SIZE 0
static const unsigned int GLUE_POP_FPSTACK[1] = { 0 }; // no need to pop, not a stack

static const unsigned int GLUE_POP_FPSTACK_TOSTACK[] = {
0xe24dd008, // sub sp, sp, #8
0xed8d0b00, // fstd d0, [sp]
};

static const unsigned int GLUE_POP_FPSTACK_TO_WTP[] = {
0xed850b00, // fstd d0, [r5]
0xe2855008, // add r5, r5, #8
};

#define GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE 4
static void GLUE_PUSH_VAL_AT_PX_TO_FPSTACK(void *b, int wv)
{
*(unsigned int *)b = 0xed900b00 + (wv<<16); // fldd d0, [rX]
}

#define GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE (sizeof(GLUE_POP_FPSTACK_TO_WTP) + GLUE_SET_PX_FROM_WTP_SIZE)
static void GLUE_POP_FPSTACK_TO_WTP_TO_PX(unsigned char *buf, int wv)
{
GLUE_SET_PX_FROM_WTP(buf,wv);
memcpy(buf + GLUE_SET_PX_FROM_WTP_SIZE,GLUE_POP_FPSTACK_TO_WTP,sizeof(GLUE_POP_FPSTACK_TO_WTP));
};

static const unsigned int GLUE_SET_P1_Z[] = { 0xe3a00000 }; // mov r0, #0
static const unsigned int GLUE_SET_P1_NZ[] = { 0xe3a00001 }; // mov r0, #1


static void *GLUE_realAddress(void *fn, int *size)
{
static const unsigned int sig[3] = { 0xe1a00000, 0xe1a01001, 0xe1a02002 };
unsigned char *p = (unsigned char *)fn;

while (memcmp(p,sig,sizeof(sig))) p+=4;
p+=sizeof(sig);
fn = p;

while (memcmp(p,sig,sizeof(sig))) p+=4;
*size = p - (unsigned char *)fn;
return fn;
}

static unsigned int __attribute__((unused)) glue_getscr()
{
unsigned int rv;
asm volatile ( "fmrx %0, fpscr" : "=r" (rv));
return rv;
}
static void __attribute__((unused)) glue_setscr(unsigned int v)
{
asm volatile ( "fmxr fpscr, %0" :: "r"(v));
}

void eel_enterfp(int s[2])
{
s[0] = glue_getscr();
glue_setscr(s[0] | (1<<24)); // could also do 3<<22 for RTZ
}
void eel_leavefp(int s[2])
{
glue_setscr(s[0]);
}


#endif

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save