diff --git a/README.md b/README.md
index c344ec64..367581ab 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@ Tested in
# Downloads
The current release can be found in the [vst2_bin/](vst2_bin/) folder.
-Here's a snapshot of it: [veeseevstrack_0_6_1_win64_bin-07Jul2018b.7z](dist/veeseevstrack_0_6_1_win64_bin-07Jul2018b.7z)
+Here's a snapshot of it: [veeseevstrack_0_6_1_win64_bin-08Jul2018.7z](dist/veeseevstrack_0_6_1_win64_bin-08Jul2018.7z) (64bit)
# Demo Video
@@ -178,6 +178,12 @@ The following add-on modules are statically linked with the VST plugin:
- cf.PATCH
- cf.LEDS
- cf.DAVE
+ - DHE-Modules.BoosterStage
+ - DHE-Modules.Cubic
+ - DHE-Modules.Hostage
+ - DHE-Modules.Stage
+ - DHE-Modules.Swave
+ - DHE-Modules.Upstage
- ESeries.E340
- ErraticInstruments.MPEToCV
- ErraticInstruments.QuadMPEToCV
@@ -259,6 +265,23 @@ The following add-on modules are statically linked with the VST plugin:
- LOGinstruments.Velvet
- LOGinstruments.Crystal
- modular80.Logistiker
+ - mscHack.MasterClockx4
+ - mscHack.Seq_3x16x16
+ - mscHack.SEQ_6x32x16
+ - mscHack.Seq_Triad2
+ - mscHack.SEQ_Envelope_8
+ - mscHack.Maude_221
+ - mscHack.ARP700
+ - mscHack.SynthDrums
+ - mscHack.XFade
+ - mscHack.Mix_1x4_Stereo
+ - mscHack.Mix_2x4_Stereo
+ - mscHack.Mix_4x4_Stereo
+ - mscHack.Mix_24_4_4
+ - mscHack.StepDelay
+ - mscHack.PingPong
+ - mscHack.Osc_3Ch
+ - mscHack.Compressor
- mtsch_plugins.Sum
- mtsch_plugins.Rationals
- mtsch_plugins.TriggerPanic
diff --git a/dep/dep.7z b/dep/dep.7z
index 04de6c09..59ddbbb7 100644
Binary files a/dep/dep.7z and b/dep/dep.7z differ
diff --git a/include/global_ui.hpp b/include/global_ui.hpp
index ec1a6540..8d624349 100644
--- a/include/global_ui.hpp
+++ b/include/global_ui.hpp
@@ -100,11 +100,11 @@ struct GlobalUI {
window.gFramebufferVg = NULL;
window.gPixelRatio = 1.0;
window.gWindowRatio = 1.0;
-#ifdef USE_VST2
- window.gAllowCursorLock = false;
-#else
+// #ifdef USE_VST2
+// window.gAllowCursorLock = false;
+// #else
window.gAllowCursorLock = true;
-#endif // USE_VST2
+// #endif // USE_VST2
window.windowX = 0;
window.windowY = 0;
window.windowWidth = 0;
diff --git a/makefile_lib.msvc b/makefile_lib.msvc
index 94bb5dce..a79407ab 100644
--- a/makefile_lib.msvc
+++ b/makefile_lib.msvc
@@ -1,5 +1,5 @@
#
-# Makefile for VCV rack lib + Microsoft Visual C++ 2003 / 2005 / 2008 ToolKit
+# Makefile for VCV rack lib + Microsoft Visual C++ 2003 / 2005 / 2008 / 2017 ToolKit
#
#
@@ -9,7 +9,13 @@ TARGET_BASENAME=Rack
EXTRAFLAGS= -DVERSION=0.6.1 -DARCH_WIN -D_USE_MATH_DEFINES -DRACK_HOST -DUSE_VST2 -DVST2_REPARENT_WINDOW_HACK -Iinclude/ -Idep/include
-EXTRALIBS= -LIBPATH:dep/lib/msvc/ jansson.lib
+ifeq ($(BUILD_64),y)
+EXTRALIBS= -LIBPATH:dep/lib/msvc/x64
+else
+EXTRALIBS= -LIBPATH:dep/lib/msvc/x86
+endif
+
+EXTRALIBS+= jansson.lib
# glew.lib glfw.lib gdi32.lib user32.lib kernel32.lib
PLAF_OBJ=
diff --git a/plugins/community/repos/DHE-Modules/.appveyor.yml b/plugins/community/repos/DHE-Modules/.appveyor.yml
new file mode 100644
index 00000000..4270b8b0
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/.appveyor.yml
@@ -0,0 +1,19 @@
+version: 1.0.{build}
+image: Visual Studio 2017
+test: off
+
+environment:
+ MSYSTEM: MINGW64
+
+artifacts:
+ - path: dist\*.zip
+
+cache:
+ - c:\tmp\Rack
+
+install:
+- set PATH=C:\msys64\usr\bin;%PATH%
+- pacman -S --noconfirm zip unzip mingw-w64-x86_64-cmake
+
+build_script:
+- bash -l %APPVEYOR_BUILD_FOLDER%\script\appveyor-build.sh
diff --git a/plugins/community/repos/DHE-Modules/.envrc b/plugins/community/repos/DHE-Modules/.envrc
new file mode 100644
index 00000000..e69de29b
diff --git a/plugins/community/repos/DHE-Modules/.idea/.name b/plugins/community/repos/DHE-Modules/.idea/.name
new file mode 100644
index 00000000..4a362e66
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/.idea/.name
@@ -0,0 +1 @@
+DHE_Modules
\ No newline at end of file
diff --git a/plugins/community/repos/DHE-Modules/.idea/DHE-Modules.iml b/plugins/community/repos/DHE-Modules/.idea/DHE-Modules.iml
new file mode 100644
index 00000000..a2db577e
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/.idea/DHE-Modules.iml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/community/repos/DHE-Modules/.idea/bashsupport_project.xml b/plugins/community/repos/DHE-Modules/.idea/bashsupport_project.xml
new file mode 100644
index 00000000..b76bb103
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/.idea/bashsupport_project.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/community/repos/DHE-Modules/.idea/codeStyles/Project.xml b/plugins/community/repos/DHE-Modules/.idea/codeStyles/Project.xml
new file mode 100644
index 00000000..30aa626c
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/.idea/codeStyles/Project.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/community/repos/DHE-Modules/.idea/codeStyles/codeStyleConfig.xml b/plugins/community/repos/DHE-Modules/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 00000000..a55e7a17
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/community/repos/DHE-Modules/.idea/inspectionProfiles/Project_Default.xml b/plugins/community/repos/DHE-Modules/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 00000000..146ab09b
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/community/repos/DHE-Modules/.idea/misc.xml b/plugins/community/repos/DHE-Modules/.idea/misc.xml
new file mode 100644
index 00000000..e07b970b
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/.idea/misc.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/community/repos/DHE-Modules/.idea/modules.xml b/plugins/community/repos/DHE-Modules/.idea/modules.xml
new file mode 100644
index 00000000..b42e6423
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/community/repos/DHE-Modules/.idea/runConfigurations/All_Tests.xml b/plugins/community/repos/DHE-Modules/.idea/runConfigurations/All_Tests.xml
new file mode 100644
index 00000000..aff2b59e
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/.idea/runConfigurations/All_Tests.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/community/repos/DHE-Modules/.idea/runConfigurations/Build_Linux.xml b/plugins/community/repos/DHE-Modules/.idea/runConfigurations/Build_Linux.xml
new file mode 100644
index 00000000..1122cd60
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/.idea/runConfigurations/Build_Linux.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/community/repos/DHE-Modules/.idea/vcs.xml b/plugins/community/repos/DHE-Modules/.idea/vcs.xml
new file mode 100644
index 00000000..94a25f7f
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/community/repos/DHE-Modules/CMakeLists.txt b/plugins/community/repos/DHE-Modules/CMakeLists.txt
new file mode 100644
index 00000000..f2db1c93
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/CMakeLists.txt
@@ -0,0 +1,66 @@
+cmake_minimum_required(VERSION 3.9)
+project(DHE_Modules DESCRIPTION "DHE Modules for VCV Rack" VERSION 0.0.0)
+
+set(CMAKE_OSX_DEPLOYMENT_TARGET 10.7)
+if (DEFINED ENV{RACK_DIR})
+ set(RACK_DIR "$ENV{RACK_DIR}")
+endif (DEFINED ENV{RACK_DIR})
+if (NOT DEFINED RACK_DIR)
+ set(RACK_DIR ../..)
+endif (NOT DEFINED RACK_DIR)
+
+add_library(rack INTERFACE)
+target_include_directories(rack
+ INTERFACE
+ ${RACK_DIR}/include
+ ${RACK_DIR}/dep/include
+ )
+target_compile_features(rack INTERFACE cxx_std_11)
+target_compile_options(rack INTERFACE -stdlib=libc++)
+
+add_library(plugin SHARED
+ src/gui/booster-stage-widget.h
+ src/gui/booster-stage-widget.cpp
+ src/gui/module-widget.h
+ src/gui/module-widget.cpp
+ src/gui/hostage-widget.h
+ src/gui/hostage-widget.cpp
+ src/gui/stage-widget.h
+ src/gui/stage-widget.cpp
+ src/gui/swave-widget.h
+ src/gui/swave-widget.cpp
+ src/gui/upstage-widget.h
+ src/gui/upstage-widget.cpp
+ src/modules/booster-stage-module.h
+ src/modules/hostage-module.h
+ src/modules/stage-module.h
+ src/modules/swave-module.h
+ src/modules/upstage-module.h
+ src/plugin/dhe-modules.h
+ src/plugin/dhe-modules.cpp
+ src/util/controls.h
+ src/util/d-flip-flop.h
+ src/util/d-latch.h
+ src/util/range.h
+ src/util/latch.h
+ src/util/ramp.h
+ src/util/sigmoid.h
+ src/util/mode.h)
+target_link_libraries(plugin
+ PUBLIC -stdlib=libc++
+ PUBLIC rack
+ PRIVATE "-undefined dynamic_lookup"
+ )
+target_include_directories(plugin PUBLIC src)
+
+add_executable(tests
+ test/catch/catch.hpp
+ test/runner/main.cpp
+ test/runner/environment.cpp
+ test/stage-tests.cpp
+ test/upstage-tests.cpp
+ )
+target_compile_features(tests PUBLIC cxx_std_11)
+target_compile_options(tests PUBLIC -stdlib=libc++)
+target_include_directories(tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/test ${CMAKE_CURRENT_SOURCE_DIR}/src)
+target_link_libraries(tests PUBLIC rack -stdlib=libc++)
\ No newline at end of file
diff --git a/plugins/community/repos/DHE-Modules/DHE-Modules.json b/plugins/community/repos/DHE-Modules/DHE-Modules.json
new file mode 100644
index 00000000..f951a11a
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/DHE-Modules.json
@@ -0,0 +1,10 @@
+{
+ "name": "DHE Modules",
+ "author": "Dale Emery",
+ "license": "MIT",
+ "contactEmail": "dale@dhemery.com",
+ "manualUrl": "https://dhemery.github.io/DHE-Modules/",
+ "sourceUrl": "https://github.com/dhemery/DHE-Modules/",
+ "donateUrl": "https://paypal.me/DaleHEmery/",
+ "latestVersion": "0.6.0"
+}
diff --git a/plugins/community/repos/DHE-Modules/LICENSE.txt b/plugins/community/repos/DHE-Modules/LICENSE.txt
new file mode 100644
index 00000000..610c6f99
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/LICENSE.txt
@@ -0,0 +1,7 @@
+Copyright 2018 Dale H. Emery
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/plugins/community/repos/DHE-Modules/Makefile b/plugins/community/repos/DHE-Modules/Makefile
new file mode 100644
index 00000000..86454082
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/Makefile
@@ -0,0 +1,34 @@
+SLUG = DHE-Modules
+VERSION = 0.6.0
+RACK_DIR ?= ../..
+
+FLAGS += -I./src
+CFLAGS +=
+CXXFLAGS +=
+LDFLAGS +=
+
+SOURCES = $(wildcard src/*/*.cpp)
+
+DISTRIBUTABLES += $(wildcard LICENSE*) res
+
+include $(RACK_DIR)/plugin.mk
+
+MODULE_OBJECTS = $(patsubst %, build/%.o, $(MODULE_SOURCES))
+
+TEST_SOURCES = $(wildcard test/runner/*.cpp test/*.cpp)
+TEST_OBJECTS = $(patsubst %, build/%.o, $(TEST_SOURCES))
+
+$(TEST_OBJECTS): FLAGS+= -I./test
+
+build/test/runner/main: $(TEST_OBJECTS)
+ $(CXX) -o $@ $^
+
+test: build/test/runner/main
+ $<
+
+run: dist
+ cp -R dist/DHE-Modules $(RACK_DIR)/plugins
+ make -C $(RACK_DIR) run
+
+gui:
+ cd gui && rake clobber all
diff --git a/plugins/community/repos/DHE-Modules/README.md b/plugins/community/repos/DHE-Modules/README.md
new file mode 100644
index 00000000..11aa34d3
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/README.md
@@ -0,0 +1,5 @@
+# DHE Modules
+
+VCV Rack modules from Dale Emery.
+
+See the [DHE Modules Site](https://dhemery.github.io/DHE-Modules/) for details.
diff --git a/plugins/community/repos/DHE-Modules/docker/make-all.sh b/plugins/community/repos/DHE-Modules/docker/make-all.sh
new file mode 100644
index 00000000..af665c21
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/docker/make-all.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+set -o errexit
+set -o nounset
+set -o xtrace
+
+cd "$(dirname "$0")/"
+docker image build -t rack:env rack-env/
+docker image build -t rack:dep rack-dep/
+docker image build -t rack:build rack-build/
diff --git a/plugins/community/repos/DHE-Modules/docker/rack-build/Dockerfile b/plugins/community/repos/DHE-Modules/docker/rack-build/Dockerfile
new file mode 100644
index 00000000..797e2d3d
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/docker/rack-build/Dockerfile
@@ -0,0 +1,4 @@
+FROM rack:dep
+RUN cd "${VCV_RACK_DIR}" && make
+VOLUME /out
+CMD bash
diff --git a/plugins/community/repos/DHE-Modules/docker/rack-dep/Dockerfile b/plugins/community/repos/DHE-Modules/docker/rack-dep/Dockerfile
new file mode 100644
index 00000000..2805a424
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/docker/rack-dep/Dockerfile
@@ -0,0 +1,9 @@
+FROM rack:env
+ENV VCV_RACK_DIR=/build/Rack
+RUN mkdir -p "${VCV_RACK_DIR}" \
+ && git clone -n https://github.com/VCVRack/Rack.git "${VCV_RACK_DIR}" || true \
+ && cd "${VCV_RACK_DIR}" \
+ && git checkout master \
+ && git pull \
+ && git submodule update --init --recursive \
+ && make dep > /dev/null
diff --git a/plugins/community/repos/DHE-Modules/docker/rack-env/Dockerfile b/plugins/community/repos/DHE-Modules/docker/rack-env/Dockerfile
new file mode 100644
index 00000000..05af0b47
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/docker/rack-env/Dockerfile
@@ -0,0 +1,30 @@
+FROM ubuntu
+RUN apt-get update \
+ && apt-get install -y --no-install-recommends software-properties-common \
+ && add-apt-repository -y ppa:ubuntu-toolchain-r/test \
+ && apt-get update \
+ && apt-get install -y --no-install-recommends \
+ autoconf \
+ automake \
+ cmake \
+ curl \
+ g++ \
+ git \
+ libasound2-dev \
+ libgl1-mesa-dev \
+ libglu1-mesa-dev \
+ libgtk2.0-dev \
+ libtool \
+ libudev-dev \
+ libxcursor-dev \
+ libxinerama-dev\
+ libxrandr-dev \
+ make \
+ tar \
+ unzip \
+ wget \
+ zip \
+ zlib1g-dev \
+ && add-apt-repository -yr ppa:ubuntu-toolchain-r/test \
+ && apt-get autoremove --purge \
+ && apt-get clean
diff --git a/plugins/community/repos/DHE-Modules/gui/.idea/.rakeTasks b/plugins/community/repos/DHE-Modules/gui/.idea/.rakeTasks
new file mode 100644
index 00000000..c98798a2
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/gui/.idea/.rakeTasks
@@ -0,0 +1,7 @@
+
+
diff --git a/plugins/community/repos/DHE-Modules/gui/.idea/encodings.xml b/plugins/community/repos/DHE-Modules/gui/.idea/encodings.xml
new file mode 100644
index 00000000..97626ba4
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/gui/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/community/repos/DHE-Modules/gui/.idea/gui.iml b/plugins/community/repos/DHE-Modules/gui/.idea/gui.iml
new file mode 100644
index 00000000..3c4780fe
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/gui/.idea/gui.iml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/community/repos/DHE-Modules/gui/.idea/inspectionProfiles/Project_Default.xml b/plugins/community/repos/DHE-Modules/gui/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 00000000..9466ecad
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/gui/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/community/repos/DHE-Modules/gui/.idea/misc.xml b/plugins/community/repos/DHE-Modules/gui/.idea/misc.xml
new file mode 100644
index 00000000..e38033af
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/gui/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/plugins/community/repos/DHE-Modules/gui/.idea/modules.xml b/plugins/community/repos/DHE-Modules/gui/.idea/modules.xml
new file mode 100644
index 00000000..c2b3c36a
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/gui/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/community/repos/DHE-Modules/gui/.idea/vcs.xml b/plugins/community/repos/DHE-Modules/gui/.idea/vcs.xml
new file mode 100644
index 00000000..6c0b8635
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/gui/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/community/repos/DHE-Modules/gui/Gemfile b/plugins/community/repos/DHE-Modules/gui/Gemfile
new file mode 100644
index 00000000..b2b62dba
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/gui/Gemfile
@@ -0,0 +1,5 @@
+source 'https://rubygems.org'
+
+gem 'jekyll', '~> 3.7'
+gem 'rake', '~> 12.3'
+gem 'color', '~> 1.8'
diff --git a/plugins/community/repos/DHE-Modules/gui/Gemfile.lock b/plugins/community/repos/DHE-Modules/gui/Gemfile.lock
new file mode 100644
index 00000000..14dff40e
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/gui/Gemfile.lock
@@ -0,0 +1,67 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ addressable (2.5.2)
+ public_suffix (>= 2.0.2, < 4.0)
+ color (1.8)
+ colorator (1.1.0)
+ concurrent-ruby (1.0.5)
+ em-websocket (0.5.1)
+ eventmachine (>= 0.12.9)
+ http_parser.rb (~> 0.6.0)
+ eventmachine (1.2.5)
+ ffi (1.9.23)
+ forwardable-extended (2.6.0)
+ http_parser.rb (0.6.0)
+ i18n (0.9.5)
+ concurrent-ruby (~> 1.0)
+ jekyll (3.7.3)
+ addressable (~> 2.4)
+ colorator (~> 1.0)
+ em-websocket (~> 0.5)
+ i18n (~> 0.7)
+ jekyll-sass-converter (~> 1.0)
+ jekyll-watch (~> 2.0)
+ kramdown (~> 1.14)
+ liquid (~> 4.0)
+ mercenary (~> 0.3.3)
+ pathutil (~> 0.9)
+ rouge (>= 1.7, < 4)
+ safe_yaml (~> 1.0)
+ jekyll-sass-converter (1.5.2)
+ sass (~> 3.4)
+ jekyll-watch (2.0.0)
+ listen (~> 3.0)
+ kramdown (1.16.2)
+ liquid (4.0.0)
+ listen (3.1.5)
+ rb-fsevent (~> 0.9, >= 0.9.4)
+ rb-inotify (~> 0.9, >= 0.9.7)
+ ruby_dep (~> 1.2)
+ mercenary (0.3.6)
+ pathutil (0.16.1)
+ forwardable-extended (~> 2.6)
+ public_suffix (3.0.2)
+ rake (12.3.0)
+ rb-fsevent (0.10.3)
+ rb-inotify (0.9.10)
+ ffi (>= 0.5.0, < 2)
+ rouge (3.1.1)
+ ruby_dep (1.5.0)
+ safe_yaml (1.0.4)
+ sass (3.5.5)
+ sass-listen (~> 4.0.0)
+ sass-listen (4.0.0)
+ rb-fsevent (~> 0.9, >= 0.9.4)
+ rb-inotify (~> 0.9, >= 0.9.7)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ color (~> 1.8)
+ jekyll (~> 3.7)
+ rake (~> 12.3)
+
+BUNDLED WITH
+ 1.16.1
diff --git a/plugins/community/repos/DHE-Modules/gui/Rakefile b/plugins/community/repos/DHE-Modules/gui/Rakefile
new file mode 100644
index 00000000..fdaa03ab
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/gui/Rakefile
@@ -0,0 +1,67 @@
+require 'rake'
+require 'rake/clean'
+require 'pathname'
+
+build_dir = Pathname('_build')
+module_dest_dir = Pathname('../res')
+image_dest_dir = Pathname('../images')
+
+directory module_dest_dir
+directory image_dest_dir
+
+control_build_dir = build_dir / 'controls'
+control_build_files = "#{control_build_dir}/**/*.svg"
+control_build_file_to_dest_file = "%{^#{control_build_dir}/,#{module_dest_dir}/}p"
+
+image_build_dir = build_dir / 'images'
+image_build_files = "#{image_build_dir}/**/*.svg"
+image_build_file_to_dest_file = "%{^#{image_build_dir}/,#{image_dest_dir}/}p"
+
+panel_build_dir = build_dir / 'panels'
+panel_build_files = "#{panel_build_dir}/**/*.svg"
+panel_build_file_to_dest_file = "%{^#{panel_build_dir}/,#{module_dest_dir}/}X/panel.svg"
+
+module_source_dir = 'panels'
+module_source_files = FileList["#{module_source_dir}/**/*.svg"]
+module_source_file_to_dest_dir = "%{^#{module_source_dir}/,#{module_dest_dir}/}X"
+module_dest_dirs = module_source_files.pathmap(module_source_file_to_dest_dir)
+module_dest_dirs.each { |dir| directory dir }
+
+desc 'Compile the SVG images'
+task :compile do
+ sh 'bundle exec jekyll b --trace'
+end
+
+task controls: [:compile] + module_dest_dirs do
+ FileList[control_build_files].each do |build_file|
+ dest_file = build_file.pathmap(control_build_file_to_dest_file)
+ sh "xmllint --format #{build_file} > #{dest_file}"
+ end
+end
+
+task panels: [:compile] + module_dest_dirs do
+ FileList[panel_build_files].each do |build_file|
+ dest_file = build_file.pathmap(panel_build_file_to_dest_file)
+ convert_text_to_paths(build_file, dest_file)
+ end
+end
+
+task images: [:compile, image_dest_dir] do
+ FileList[image_build_files].each do |build_file|
+ dest_file = build_file.pathmap(image_build_file_to_dest_file)
+ convert_text_to_paths(build_file, dest_file)
+ end
+end
+
+task all: [:controls, :panels, :images]
+task default: :all
+
+CLEAN.include build_dir
+CLOBBER.include module_dest_dir, image_dest_dir
+
+def convert_text_to_paths(source, dest)
+ source = Pathname(source).expand_path.to_s
+ dest = Pathname(dest).expand_path.to_s
+ sh "/Applications/Inkscape.app/Contents/Resources/script --export-text-to-path --export-plain-svg=#{dest} #{source}"
+end
+
diff --git a/plugins/community/repos/DHE-Modules/gui/_config.yaml b/plugins/community/repos/DHE-Modules/gui/_config.yaml
new file mode 100644
index 00000000..b766ca1e
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/gui/_config.yaml
@@ -0,0 +1,6 @@
+destination: _build
+incremental: false
+exclude:
+ - Gemfile
+ - Gemfile.lock
+ - Rakefile
diff --git a/plugins/community/repos/DHE-Modules/gui/_layouts/control.svg b/plugins/community/repos/DHE-Modules/gui/_layouts/control.svg
new file mode 100644
index 00000000..65d82a5e
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/gui/_layouts/control.svg
@@ -0,0 +1,8 @@
+{% assign width = page.width %}
+{% assign height = page.height %}
+
+
+ {{ content }}
+
+
diff --git a/plugins/community/repos/DHE-Modules/gui/_layouts/panel.svg b/plugins/community/repos/DHE-Modules/gui/_layouts/panel.svg
new file mode 100644
index 00000000..7ce46338
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/gui/_layouts/panel.svg
@@ -0,0 +1,8 @@
+{% assign height = 128.5 %}
+{% assign width = page.width | hp_to_mm %}
+
+
+ {{ page | panel }}
+ {{ content }}
+
+
diff --git a/plugins/community/repos/DHE-Modules/gui/_plugins/controls.rb b/plugins/community/repos/DHE-Modules/gui/_plugins/controls.rb
new file mode 100644
index 00000000..06af973d
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/gui/_plugins/controls.rb
@@ -0,0 +1,213 @@
+class Bounded
+ attr_reader :top, :right, :bottom, :left
+
+ def initialize(top:, right:, bottom:, left:)
+ @top = top
+ @right = right
+ @bottom = bottom
+ @left = left
+ end
+
+ def width
+ @right - @left
+ end
+
+ def height
+ @bottom - @top
+ end
+
+ def center
+ OpenStruct.new(x: width / 2.0 + left, y: height / 2.0 + top)
+ end
+
+ def translate(delta_x: 0, delta_y: 0)
+ @top += delta_y
+ @right += delta_x
+ @bottom += delta_y
+ @left += delta_x
+ end
+
+ def move_center_to(x: center.x, y: center.y)
+ delta_x = x - center.x
+ delta_y = y - center.y
+ translate(delta_x: delta_x, delta_y: delta_y)
+ end
+end
+
+class Control < Bounded
+ def initialize(x:, y:, width:, height: width)
+ super(top: y - height / 2.0, right: x + width / 2.0, bottom: y + height / 2.0, left: x - width / 2.0)
+ end
+end
+
+class RoundControl < Control
+ attr_reader :diameter
+
+ def initialize(x:, y:, diameter:)
+ super(x: x, y: y, width: diameter)
+ @diameter = diameter
+ end
+
+ def radius
+ diameter / 2.0
+ end
+end
+
+class ButtonControl < RoundControl
+ DIAMETER = 6.0
+
+ def initialize(x: 0.0, y: 0.0, style:, state:, dark:, light:)
+ super(x: x, y: y, diameter: DIAMETER)
+ @style = style.to_sym
+ @state = state.to_sym
+ @button_color = @style == :dark ? dark : light
+ if @style == :dark
+ @state_color = @state == :on ? light : dark
+ else
+ @state_color = @state == :on ? dark : light
+ end
+ end
+
+ def name
+ "button-#{@style}-#{@state}"
+ end
+
+ def align(padding, alignment, other)
+ new_x = case alignment
+ when :right_of
+ other.right + padding + radius
+ when :left_of
+ other.left - padding - radius
+ else
+ center.x
+ end
+ move_center_to(x: new_x, y: other.center.y)
+ end
+
+ def svg
+ stroke_width = diameter / 6.0
+ circle_diameter = diameter - stroke_width
+ circle_radius = circle_diameter / 2.0
+ %Q[
+
+ ]
+ end
+end
+
+class KnobControl < RoundControl
+ DIAMETER = 12.7
+
+ def initialize(x: 0.0, y: 0.0, knob_color:, pointer_color:)
+ super(x: x, y: y, diameter: 12.7)
+ @knob_color = knob_color
+ @pointer_color = pointer_color
+ end
+
+ def name
+ 'knob-large'
+ end
+
+ def svg
+ pointer_width = radius / 8.0
+ pointer_length = radius - pointer_width
+ %Q[
+
+
+
+
+ ]
+ end
+end
+
+class PortControl < RoundControl
+ DIAMETER = 8.4
+
+ def initialize(x: 0.0, y: 0.0, metal_color:, shadow_color:)
+ super(x: x, y: y, diameter: 8.4)
+ @metal_color = metal_color
+ @shadow_color = shadow_color
+ end
+
+ def name
+ 'port'
+ end
+
+ def svg
+ stroke_width = diameter * 0.025
+ sleeve_diameter = diameter - stroke_width
+ step = sleeve_diameter / 7.0
+ sleeve_radius = sleeve_diameter / 2.0
+ ring_radius = sleeve_radius - step
+ tip_radius = ring_radius - step
+ %Q[
+
+
+
+
+
+ ]
+ end
+end
+
+class SwitchControl < Control
+ WIDTH = 3.0
+
+ def initialize(x: 0.0, y: 0.0, positions:, state:, dark:, light:)
+ super(x: x, y: y, width: WIDTH, height: positions * WIDTH)
+ @positions = positions
+ @state = state
+ @dark = dark
+ @light = light
+ @position =
+ case @state
+ when :high
+ 1.0
+ when :low
+ -1.0
+ else
+ 0.0
+ end
+ end
+
+ def name
+ "switch-#{@positions}-#{@state}"
+ end
+
+ def svg
+ box_stroke_width = width / 8.0
+ interior_inset = box_stroke_width / 2.0
+
+ box_width = width - box_stroke_width
+ box_height = height - box_stroke_width
+ box_left = -width / 2.0 + interior_inset
+ box_top = -height / 2.0 + interior_inset
+
+ interior_width = box_width - box_stroke_width
+ interior_height = box_height - box_stroke_width
+ corner_radius = interior_inset
+
+ knurl_stroke_width = 0.25
+ knurl_inset = knurl_stroke_width * 2.0
+ knurl_length = interior_width - knurl_inset
+ knurl_left = knurl_length / -2.0
+ knurl_right = knurl_left + knurl_length
+ knurl_spacing = knurl_stroke_width * 2.0
+
+ lever_height = knurl_spacing * 4.0 + knurl_stroke_width
+ lever_inset = knurl_stroke_width
+ lever_distance = (interior_height - lever_height) / 2.0 - lever_inset
+ lever_offset = lever_distance * -@position
+ lever = (-2..2)
+ .map {|index| knurl_spacing * index + lever_offset}
+ .map {|y| %Q[ ]}
+ .join("\n")
+ %Q[
+
+
+ #{lever}
+
+ ]
+ end
+end
diff --git a/plugins/community/repos/DHE-Modules/gui/_plugins/module-generator.rb b/plugins/community/repos/DHE-Modules/gui/_plugins/module-generator.rb
new file mode 100644
index 00000000..6fd27dc0
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/gui/_plugins/module-generator.rb
@@ -0,0 +1,78 @@
+require 'rake'
+require 'color'
+require_relative 'controls'
+require_relative 'page-color'
+
+module DHE
+ SOURCE_DIR = '/panels'
+ class PageWithoutAFile < Jekyll::Page
+ def read_yaml(*)
+ @data ||= {}
+ end
+ end
+
+ class Generator < Jekyll::Generator
+ include DHE::PageColor
+
+ def generate(site)
+ module_pages = site.pages.select {|page| page.url.start_with? SOURCE_DIR}
+ module_pages.each do |module_page|
+ site.pages += control_pages(module_page)
+ site.pages << image_page(module_page)
+ end
+ end
+
+ def control_page(module_page, control)
+ page = PageWithoutAFile.new(module_page.site, __dir__, module_page.url.pathmap("%{^#{SOURCE_DIR},controls}X"), control.name.ext('svg'))
+ page.data['layout'] = 'control'
+ page.data['width'] = control.width
+ page.data['height'] = control.height
+ page.content = control.svg
+ page
+ end
+
+ def control_pages(module_page)
+ controls(module_page).map {|control| control_page(module_page, control)}
+ end
+
+ def controls(page)
+ page.data['controls'].flat_map {|type, variants| send(type, page, variants)}
+ end
+
+ def image_page(module_page)
+ page = PageWithoutAFile.new(module_page.site, __dir__, module_page.url.pathmap("%{^#{SOURCE_DIR},images}d"), module_page.name)
+ page.data.merge!(module_page.data)
+ page.data['draw_controls'] = true
+ page.data['dark'] = dark(page)
+ page.data['light'] = light(page)
+ page.content = module_page.content
+ page
+ end
+
+ def buttons(page, variants)
+ variants.flat_map do |style|
+ [:off, :on].map do |state|
+ ButtonControl.new(style: style, state: state, dark: dark(page), light: light(page))
+ end
+ end
+ end
+
+ def ports(page, _)
+ PortControl.new(metal_color: light(page), shadow_color: dark(page))
+ end
+
+ def knobs(page, _)
+ KnobControl.new(knob_color: dark(page), pointer_color: light(page))
+ end
+
+ def switches(page, variants)
+ variants.flat_map do |positions|
+ states = [:high, :low]
+ states << :mid if positions == 3
+ states.map do |state|
+ SwitchControl.new(positions: positions, state: state, dark: dark(page), light: light(page))
+ end
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/plugins/community/repos/DHE-Modules/gui/_plugins/page-color.rb b/plugins/community/repos/DHE-Modules/gui/_plugins/page-color.rb
new file mode 100644
index 00000000..c7350193
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/gui/_plugins/page-color.rb
@@ -0,0 +1,21 @@
+module DHE
+ module PageColor
+ def hslcolor(page)
+ Color::HSL.new(*page['color'])
+ end
+
+ def rgbhex(color)
+ "##{color.to_rgb.hex}"
+ end
+
+ def light(page)
+ hsl = hslcolor(page)
+ hsl.l = 0.97
+ rgbhex(hsl)
+ end
+
+ def dark(page)
+ rgbhex(hslcolor(page))
+ end
+ end
+end
\ No newline at end of file
diff --git a/plugins/community/repos/DHE-Modules/gui/_plugins/panel-filters.rb b/plugins/community/repos/DHE-Modules/gui/_plugins/panel-filters.rb
new file mode 100644
index 00000000..f5f270ec
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/gui/_plugins/panel-filters.rb
@@ -0,0 +1,244 @@
+require 'ostruct'
+require_relative 'page-color'
+require_relative 'controls'
+
+module PanelFilters
+ include DHE::PageColor
+
+ MM_PER_INCH = 25.4
+ PX_PER_INCH = 75.0
+ PX_PER_MM = PX_PER_INCH / MM_PER_INCH
+ MM_PER_HP = 5.08
+
+ STROKE_WIDTH = 0.35
+ STROKE_INSET = STROKE_WIDTH / 2.0
+ PADDING = 1.0
+
+ PLUGIN_LABEL_INSET = 9.0
+ PANEL_HEIGHT = 128.5
+
+ PLUGIN_FONT = 12.0 / PX_PER_MM
+ LARGE_FONT = 9.0 / PX_PER_MM
+ SMALL_FONT = 7.0 / PX_PER_MM
+
+ class Text
+ ASCENT_RATIO = 2.0 / 3.0 # For Proxima Nova font
+
+ def initialize(text:, size:, color:)
+ @text = text
+ @size = size
+ @color = color
+ end
+
+ def ascent
+ @size * ASCENT_RATIO
+ end
+
+ def descent
+ @size - ascent
+ end
+
+ def svg(x:, y:, attributes:)
+ %Q[#{@text} ]
+ end
+ end
+
+ class Label < Bounded
+ ALIGNMENT_ATTRIBUTES_TEMPLATE = %[dominant-baseline="%s" text-anchor="%s"]
+ ALIGNMENT_ATTRIBUTES = {
+ above: ALIGNMENT_ATTRIBUTES_TEMPLATE % %w{baseline middle},
+ below: ALIGNMENT_ATTRIBUTES_TEMPLATE % %w{hanging middle},
+ right: ALIGNMENT_ATTRIBUTES_TEMPLATE % %w{middle start},
+ }
+
+ def initialize(text, padding, alignment, control)
+ @text = text
+ @alignment = alignment
+ case alignment
+ when :above
+ @x = control.center.x
+ @y = control.top - padding
+ when :below
+ @x = control.center.x
+ @y = control.bottom + padding
+ when :right_of
+ @x = control.right + padding
+ @y = control.center.y
+ else
+ @x = control.center.x
+ @y = control.center.y
+ end
+ super(top: @y - @text.ascent, right: @x, bottom: @y + @text.descent, left: @x)
+ end
+
+ def svg
+ @text.svg(x: @x, y: @y, attributes: ALIGNMENT_ATTRIBUTES[@alignment])
+ end
+ end
+
+ class Box < Bounded
+ CORNER_RADIUS = 1.0
+ BUFFER = PADDING + STROKE_INSET
+
+ def initialize(content_bounds:, border_color:, background_color:)
+ super(top: content_bounds.top - BUFFER, right: content_bounds.right + BUFFER, bottom: content_bounds.bottom + BUFFER, left: content_bounds.left - BUFFER)
+ @border_color = border_color
+ @background_color = background_color
+ end
+
+ def svg
+ %Q[
+
+ ]
+ end
+
+ def self.around(content:, border_color:, background_color:)
+ content_bounds = Bounded.new(top: content.map(&:top).min, right: content.map(&:right).max, bottom: content.map(&:bottom).max, left: content.map(&:left).min)
+ Box.new(content_bounds: content_bounds, border_color: border_color, background_color: background_color)
+ end
+ end
+
+ def button(page, x, y, label)
+ dark = dark(page)
+ light = light(page)
+ button = ButtonControl.new(x: x, y: y, style: :dark, state: :off, dark: dark, light: light)
+ label_text = Text.new(text: label, color: dark, size: SMALL_FONT)
+ items = [Label.new(label_text, PADDING, :above, button)]
+ items << button if page['draw_controls']
+ items.map(&:svg).join("\n")
+ end
+
+ def large_knob(page, x, y, label)
+ dark = dark(page)
+ light = light(page)
+ knob = KnobControl.new(x: x, y: y, knob_color: dark, pointer_color: light)
+ label_text = Text.new(text: label, color: dark, size: LARGE_FONT)
+ items = [Label.new(label_text, PADDING, :above, knob)]
+ items << knob if page['draw_controls']
+ items.map(&:svg).join("\n")
+ end
+
+ def cv(page, x, y)
+ dark = dark(page)
+ light = light(page)
+ draw = page['draw_controls']
+ port = PortControl.new(x: x, y: y, metal_color: light, shadow_color: dark)
+ label_text = Text.new(text: 'CV', color: dark, size: SMALL_FONT)
+ items = [Label.new(label_text, PADDING, :above, port)]
+ items << port if draw
+ items.map(&:svg).join("\n")
+ end
+
+ def port_button(port_x:, port_y:, label:, button_position:, foreground_color:, background_color:, label_color:, metal_color:, shadow_color:, button_style:, draw:)
+ port = PortControl.new(x: port_x, y: port_y, metal_color: metal_color, shadow_color: shadow_color)
+ label_text = Text.new(text: label, color: label_color, size: SMALL_FONT)
+ label = Label.new(label_text, PADDING, :above, port)
+ button = ButtonControl.new(style: button_style, state: :off, dark: shadow_color, light: metal_color)
+ button.align(PADDING, button_position, port)
+ box = Box.around(content: [port, label, button], border_color: foreground_color, background_color: background_color)
+ items = [box, label]
+ items += [port, button] if draw
+ items.map(&:svg).join("\n")
+ end
+
+ def in_port_button(page, x, y, label)
+ dark = dark(page)
+ light = light(page)
+ draw = page['draw_controls']
+ port_button(port_x: x, port_y: y, label: label, button_position: :right_of, foreground_color: dark, background_color: light, label_color: dark, metal_color: light, shadow_color: dark, button_style: :dark, draw: draw)
+ end
+
+ def out_port_button(page, x, y, label)
+ dark = dark(page)
+ light = light(page)
+ draw = page['draw_controls']
+ port_button(port_x: x, port_y: y, label: label, button_position: :left_of, foreground_color: dark, background_color: dark, label_color: light, metal_color: light, shadow_color: dark, button_style: :light, draw: draw)
+ end
+
+ def port(x:, y:, foreground_color:, background_color:, label:, label_color:, metal_color:, shadow_color:, draw:)
+ port = PortControl.new(x: x, y: y, metal_color: metal_color, shadow_color: shadow_color)
+ label_text = Text.new(text: label, color: label_color, size: SMALL_FONT)
+ label = Label.new(label_text, PADDING, :above, port)
+ box = Box.around(content: [port, label], border_color: foreground_color, background_color: background_color)
+ items = [box, label]
+ items << port if draw
+ items.map(&:svg).join("\n")
+ end
+
+ def in_port(page, x, y, label)
+ dark = dark(page)
+ light = light(page)
+ draw = page ['draw_controls']
+ port(x: x, y: y, foreground_color: dark, background_color: light, label: label, label_color: dark, metal_color: light, shadow_color: dark, draw: draw)
+ end
+
+ def out_port(page, x, y, label)
+ dark = dark(page)
+ light = light(page)
+ draw = page ['draw_controls']
+ port(x: x, y: y, foreground_color: dark, background_color: dark, label: label, label_color: light, metal_color: light, shadow_color: dark, draw: draw)
+ end
+
+ def duration_switch(page, x, y)
+ switch(page, x, y, :mid, '100', '1', '10')
+ end
+
+ def polarity_switch(page, x, y)
+ switch(page, x, y, :high, 'UNI', 'BI',)
+ end
+
+ def shape_switch(page, x, y)
+ switch(page, x, y, :low, 'S', 'J',)
+ end
+
+ def switch(page, x, y, position, high, low, mid = nil)
+ dark = dark(page)
+ light = light(page)
+ draw = page['draw_controls']
+ switch = SwitchControl.new(x: x, y: y, positions: mid ? 3 : 2, dark: dark, light: light, state: position.to_sym)
+ high_text = Text.new(text: high, size: SMALL_FONT, color: dark)
+ low_text = Text.new(text: low, size: SMALL_FONT, color: dark)
+ items = [
+ Label.new(high_text, PADDING + STROKE_INSET, :above, switch),
+ Label.new(low_text, PADDING + STROKE_INSET, :below, switch),
+ ]
+ if mid
+ mid_text = Text.new(text: mid, size: SMALL_FONT, color: dark)
+ items << Label.new(mid_text, PADDING / 2.0 + STROKE_INSET, :right_of, switch)
+ end
+ items << switch if draw
+ items.map(&:svg).join("\n")
+ end
+
+ def panel(page)
+ dark = dark(page)
+ light = light(page)
+ panel = Bounded.new(top: 0.0, right: width(page), bottom: PANEL_HEIGHT, left: 0.0)
+ name_label_text = Text.new(text: page['title'], color: dark, size: PLUGIN_FONT)
+ name_label = Label.new(name_label_text, -PLUGIN_LABEL_INSET, :above, panel)
+ author_label_text = Text.new(text: 'DHE', color: dark, size: PLUGIN_FONT)
+ author_label = Label.new(author_label_text, -PLUGIN_LABEL_INSET, :below, panel)
+ box = %Q[ ]
+ box + [name_label, author_label].map(&:svg).join("\n")
+ end
+
+ def connector(page, x1, y1, x2, y2)
+ %Q[ ]
+ end
+
+ def width(page)
+ hp_to_mm(page['width'])
+ end
+
+ def mm_to_px(mm)
+ mm * PX_PER_MM
+ end
+
+ def hp_to_mm(hp)
+ hp * MM_PER_HP
+ end
+end
+
+Liquid::Template.register_filter(PanelFilters)
diff --git a/plugins/community/repos/DHE-Modules/gui/panels/booster-stage.svg b/plugins/community/repos/DHE-Modules/gui/panels/booster-stage.svg
new file mode 100644
index 00000000..a1f348a5
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/gui/panels/booster-stage.svg
@@ -0,0 +1,39 @@
+---
+layout: panel
+width: 8
+title: BOOSTER STAGE
+color: [0,100,30]
+controls:
+ buttons: [dark, light]
+ knobs: []
+ ports: []
+ switches: [2, 3]
+---
+{% assign width = page.width | hp_to_mm %}
+{% assign left_x = width | divided_by:6.0 | plus:0.333333 %}
+{% assign right_x = width | minus:left_x %}
+{% assign center_x = width | divided_by:2.0 %}
+
+{{ page | connector: left_x, 25.0, right_x, 25.0 }}
+{{ page | cv: left_x, 25.0 }}
+{{ page | large_knob: center_x, 25.0, 'LEVEL' }}
+{{ page | polarity_switch: right_x, 25 }}
+
+{{ page | connector: left_x, 43.5, right_x, 43.5 }}
+{{ page | cv: left_x, 43.5 }}
+{{ page | large_knob: center_x, 43.5, 'CURVE' }}
+{{ page | shape_switch: right_x, 43.5 }}
+
+{{ page | connector: left_x, 62.0, right_x, 62.0 }}
+{{ page | cv: left_x, 62 }}
+{{ page | large_knob: center_x, 62.0, 'DURATION' }}
+{{ page | duration_switch: right_x, 62 }}
+
+{{ page | in_port_button: left_x, 82.0, 'DEFER' }}
+{{ page | out_port_button: right_x, 82.0, 'ACTIVE' }}
+
+{{ page | in_port_button: left_x, 97.0, 'TRIG' }}
+{{ page | out_port_button: right_x, 97.0, 'EOC' }}
+
+{{ page | in_port: left_x, 112.0, 'IN' }}
+{{ page | out_port: right_x, 112.0, 'OUT' }}
diff --git a/plugins/community/repos/DHE-Modules/gui/panels/hostage.svg b/plugins/community/repos/DHE-Modules/gui/panels/hostage.svg
new file mode 100644
index 00000000..bf6505db
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/gui/panels/hostage.svg
@@ -0,0 +1,30 @@
+---
+layout: panel
+width: 5
+title: HOSTAGE
+color: [300,100,30]
+controls:
+ knobs: []
+ ports: []
+ switches: [2, 3]
+---
+{% assign width = page.width | hp_to_mm %}
+{% assign left_x = width | divided_by:4.0 | plus:0.33333 %}
+{% assign right_x = width | minus:left_x %}
+{% assign center_x = width | divided_by:2.0 %}
+
+{{ page | switch: center_x, 25, 'low', 'SUSTAIN', 'HOLD' }}
+
+{{ page | cv: left_x, 43.5 }}
+{{ page | duration_switch: right_x, 43.5 }}
+
+{{ page | large_knob: center_x, 62.0, 'DURATION' }}
+
+{{ page | in_port: left_x, 82.0, 'DEFER' }}
+{{ page | out_port: right_x, 82.0, 'ACTIVE' }}
+
+{{ page | in_port: left_x, 97.0, 'GATE' }}
+{{ page | out_port: right_x, 97.0, 'EOC' }}
+
+{{ page | in_port: left_x, 112.0, 'IN' }}
+{{ page | out_port: right_x, 112.0, 'OUT' }}
diff --git a/plugins/community/repos/DHE-Modules/gui/panels/stage.svg b/plugins/community/repos/DHE-Modules/gui/panels/stage.svg
new file mode 100644
index 00000000..c2830d8d
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/gui/panels/stage.svg
@@ -0,0 +1,26 @@
+---
+layout: panel
+width: 5
+title: STAGE
+color: [120,100,30]
+controls:
+ knobs: []
+ ports: []
+---
+{% assign width = page.width | hp_to_mm %}
+{% assign left_x = width | divided_by:4.0 | plus:0.33333 %}
+{% assign right_x = width | minus:left_x %}
+{% assign center_x = width | divided_by:2.0 %}
+
+{{ page | large_knob: center_x, 25.0, 'LEVEL' }}
+{{ page | large_knob: center_x, 43.5, 'CURVE' }}
+{{ page | large_knob: center_x, 62.0, 'DURATION' }}
+
+{{ page | in_port: left_x, 82.0, 'DEFER' }}
+{{ page | out_port: right_x, 82.0, 'ACTIVE' }}
+
+{{ page | in_port: left_x, 97.0, 'TRIG' }}
+{{ page | out_port: right_x, 97.0, 'EOC' }}
+
+{{ page | in_port: left_x, 112.0, 'IN' }}
+{{ page | out_port: right_x, 112.0, 'OUT' }}
diff --git a/plugins/community/repos/DHE-Modules/gui/panels/swave.svg b/plugins/community/repos/DHE-Modules/gui/panels/swave.svg
new file mode 100644
index 00000000..aadf5568
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/gui/panels/swave.svg
@@ -0,0 +1,19 @@
+---
+layout: panel
+width: 4
+title: SWAVE
+color: [16,100,50]
+controls:
+ knobs: []
+ ports: []
+ switches: [2]
+---
+{% assign width = page.width | hp_to_mm %}
+{% assign center_x = width | divided_by:2.0 %}
+
+{{ page | shape_switch: center_x, 25 }}
+{{ page | large_knob: center_x, 43.5, 'CURVE' }}
+{{ page | cv: center_x, 62 }}
+
+{{ page | in_port: center_x, 97.0, 'IN' }}
+{{ page | out_port: center_x, 112.0, 'OUT' }}
diff --git a/plugins/community/repos/DHE-Modules/gui/panels/upstage.svg b/plugins/community/repos/DHE-Modules/gui/panels/upstage.svg
new file mode 100644
index 00000000..b9cd24d4
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/gui/panels/upstage.svg
@@ -0,0 +1,29 @@
+---
+layout: panel
+width: 5
+title: UPSTAGE
+color: [210,100,30]
+controls:
+ buttons: [dark]
+ knobs: []
+ ports: []
+ switches: [2]
+---
+{% assign width = page.width | hp_to_mm %}
+{% assign left_x = width | divided_by:4.0 | plus:0.33333 %}
+{% assign right_x = width | minus:left_x %}
+{% assign center_x = width | divided_by:2.0 %}
+
+{{ page | large_knob: center_x, 25.0, 'LEVEL' }}
+{{ page | cv: left_x, 43.5 }}
+{{ page | polarity_switch: right_x, 43.5 }}
+
+{{ page | button: left_x, 62, 'WAIT' }}
+{{ page | button: right_x, 62, 'TRIG' }}
+
+{{ page | in_port: left_x, 82.0, 'WAIT' }}
+
+{{ page | in_port: left_x, 97.0, 'TRIG' }}
+{{ page | out_port: right_x, 97.0, 'TRIG' }}
+
+{{ page | out_port: right_x, 112.0, 'OUT' }}
diff --git a/plugins/community/repos/DHE-Modules/images/booster-stage.svg b/plugins/community/repos/DHE-Modules/images/booster-stage.svg
new file mode 100644
index 00000000..a5fa494d
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/images/booster-stage.svg
@@ -0,0 +1,769 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/images/hostage.svg b/plugins/community/repos/DHE-Modules/images/hostage.svg
new file mode 100644
index 00000000..61402103
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/images/hostage.svg
@@ -0,0 +1,562 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/images/stage.svg b/plugins/community/repos/DHE-Modules/images/stage.svg
new file mode 100644
index 00000000..24e0dac2
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/images/stage.svg
@@ -0,0 +1,449 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/images/swave.svg b/plugins/community/repos/DHE-Modules/images/swave.svg
new file mode 100644
index 00000000..700fec66
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/images/swave.svg
@@ -0,0 +1,262 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/images/upstage.svg b/plugins/community/repos/DHE-Modules/images/upstage.svg
new file mode 100644
index 00000000..dfc01a4e
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/images/upstage.svg
@@ -0,0 +1,424 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/make.objects b/plugins/community/repos/DHE-Modules/make.objects
new file mode 100644
index 00000000..c0ecabc4
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/make.objects
@@ -0,0 +1,9 @@
+ALL_OBJ= \
+ src/gui/booster-stage-widget.o \
+ src/gui/cubic-widget.o \
+ src/gui/hostage-widget.o \
+ src/gui/module-widget.o \
+ src/gui/stage-widget.o \
+ src/gui/swave-widget.o \
+ src/gui/upstage-widget.o \
+ src/plugin/dhe-modules.o
diff --git a/plugins/community/repos/DHE-Modules/makefile.msvc b/plugins/community/repos/DHE-Modules/makefile.msvc
new file mode 100644
index 00000000..1867e010
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/makefile.msvc
@@ -0,0 +1,9 @@
+SLUG=DHE-Modules
+
+include ../../../../dep/yac/install_msvc.mk
+
+EXTRAFLAGS+= -Isrc/
+
+include make.objects
+
+include ../../../build_plugin.mk
diff --git a/plugins/community/repos/DHE-Modules/res/booster-stage/button-dark-off.svg b/plugins/community/repos/DHE-Modules/res/booster-stage/button-dark-off.svg
new file mode 100644
index 00000000..f086436e
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/booster-stage/button-dark-off.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/booster-stage/button-dark-on.svg b/plugins/community/repos/DHE-Modules/res/booster-stage/button-dark-on.svg
new file mode 100644
index 00000000..60f3959f
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/booster-stage/button-dark-on.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/booster-stage/button-light-off.svg b/plugins/community/repos/DHE-Modules/res/booster-stage/button-light-off.svg
new file mode 100644
index 00000000..28b37c35
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/booster-stage/button-light-off.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/booster-stage/button-light-on.svg b/plugins/community/repos/DHE-Modules/res/booster-stage/button-light-on.svg
new file mode 100644
index 00000000..409d4766
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/booster-stage/button-light-on.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/booster-stage/knob-large.svg b/plugins/community/repos/DHE-Modules/res/booster-stage/knob-large.svg
new file mode 100644
index 00000000..9f4a531b
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/booster-stage/knob-large.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/booster-stage/panel.svg b/plugins/community/repos/DHE-Modules/res/booster-stage/panel.svg
new file mode 100644
index 00000000..03f14134
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/booster-stage/panel.svg
@@ -0,0 +1,474 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/booster-stage/port.svg b/plugins/community/repos/DHE-Modules/res/booster-stage/port.svg
new file mode 100644
index 00000000..d567be85
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/booster-stage/port.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/booster-stage/switch-2-high.svg b/plugins/community/repos/DHE-Modules/res/booster-stage/switch-2-high.svg
new file mode 100644
index 00000000..3312b0a7
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/booster-stage/switch-2-high.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/booster-stage/switch-2-low.svg b/plugins/community/repos/DHE-Modules/res/booster-stage/switch-2-low.svg
new file mode 100644
index 00000000..45b91740
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/booster-stage/switch-2-low.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/booster-stage/switch-3-high.svg b/plugins/community/repos/DHE-Modules/res/booster-stage/switch-3-high.svg
new file mode 100644
index 00000000..29c84e6e
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/booster-stage/switch-3-high.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/booster-stage/switch-3-low.svg b/plugins/community/repos/DHE-Modules/res/booster-stage/switch-3-low.svg
new file mode 100644
index 00000000..61597de8
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/booster-stage/switch-3-low.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/booster-stage/switch-3-mid.svg b/plugins/community/repos/DHE-Modules/res/booster-stage/switch-3-mid.svg
new file mode 100644
index 00000000..15a84259
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/booster-stage/switch-3-mid.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/cubic/knob-small.svg b/plugins/community/repos/DHE-Modules/res/cubic/knob-small.svg
new file mode 100644
index 00000000..24b4026a
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/cubic/knob-small.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/cubic/panel.svg b/plugins/community/repos/DHE-Modules/res/cubic/panel.svg
new file mode 100644
index 00000000..01f2dbb4
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/cubic/panel.svg
@@ -0,0 +1,273 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/cubic/port.svg b/plugins/community/repos/DHE-Modules/res/cubic/port.svg
new file mode 100644
index 00000000..71848d41
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/cubic/port.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/hostage/knob-large.svg b/plugins/community/repos/DHE-Modules/res/hostage/knob-large.svg
new file mode 100644
index 00000000..92c1ec49
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/hostage/knob-large.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/hostage/panel.svg b/plugins/community/repos/DHE-Modules/res/hostage/panel.svg
new file mode 100644
index 00000000..93d0bd21
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/hostage/panel.svg
@@ -0,0 +1,372 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/hostage/port.svg b/plugins/community/repos/DHE-Modules/res/hostage/port.svg
new file mode 100644
index 00000000..cb75a644
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/hostage/port.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/hostage/switch-2-high.svg b/plugins/community/repos/DHE-Modules/res/hostage/switch-2-high.svg
new file mode 100644
index 00000000..94c9b358
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/hostage/switch-2-high.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/hostage/switch-2-low.svg b/plugins/community/repos/DHE-Modules/res/hostage/switch-2-low.svg
new file mode 100644
index 00000000..9b7aef6f
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/hostage/switch-2-low.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/hostage/switch-3-high.svg b/plugins/community/repos/DHE-Modules/res/hostage/switch-3-high.svg
new file mode 100644
index 00000000..398c317d
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/hostage/switch-3-high.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/hostage/switch-3-low.svg b/plugins/community/repos/DHE-Modules/res/hostage/switch-3-low.svg
new file mode 100644
index 00000000..8ff9662c
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/hostage/switch-3-low.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/hostage/switch-3-mid.svg b/plugins/community/repos/DHE-Modules/res/hostage/switch-3-mid.svg
new file mode 100644
index 00000000..e0a31c00
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/hostage/switch-3-mid.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/stage/knob-large.svg b/plugins/community/repos/DHE-Modules/res/stage/knob-large.svg
new file mode 100644
index 00000000..e9179b5b
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/stage/knob-large.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/stage/panel.svg b/plugins/community/repos/DHE-Modules/res/stage/panel.svg
new file mode 100644
index 00000000..926079fd
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/stage/panel.svg
@@ -0,0 +1,308 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/stage/port.svg b/plugins/community/repos/DHE-Modules/res/stage/port.svg
new file mode 100644
index 00000000..432350ea
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/stage/port.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/swave/knob-large.svg b/plugins/community/repos/DHE-Modules/res/swave/knob-large.svg
new file mode 100644
index 00000000..9ef48fb9
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/swave/knob-large.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/swave/panel.svg b/plugins/community/repos/DHE-Modules/res/swave/panel.svg
new file mode 100644
index 00000000..20763d75
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/swave/panel.svg
@@ -0,0 +1,169 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/swave/port.svg b/plugins/community/repos/DHE-Modules/res/swave/port.svg
new file mode 100644
index 00000000..3f9e71ae
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/swave/port.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/swave/switch-2-high.svg b/plugins/community/repos/DHE-Modules/res/swave/switch-2-high.svg
new file mode 100644
index 00000000..eceab81d
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/swave/switch-2-high.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/swave/switch-2-low.svg b/plugins/community/repos/DHE-Modules/res/swave/switch-2-low.svg
new file mode 100644
index 00000000..df41e090
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/swave/switch-2-low.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/upstage/button-dark-off.svg b/plugins/community/repos/DHE-Modules/res/upstage/button-dark-off.svg
new file mode 100644
index 00000000..94efcaaa
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/upstage/button-dark-off.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/upstage/button-dark-on.svg b/plugins/community/repos/DHE-Modules/res/upstage/button-dark-on.svg
new file mode 100644
index 00000000..33868c5c
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/upstage/button-dark-on.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/upstage/knob-large.svg b/plugins/community/repos/DHE-Modules/res/upstage/knob-large.svg
new file mode 100644
index 00000000..8d98db91
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/upstage/knob-large.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/upstage/panel.svg b/plugins/community/repos/DHE-Modules/res/upstage/panel.svg
new file mode 100644
index 00000000..473cda46
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/upstage/panel.svg
@@ -0,0 +1,289 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/upstage/port.svg b/plugins/community/repos/DHE-Modules/res/upstage/port.svg
new file mode 100644
index 00000000..10208f2b
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/upstage/port.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/upstage/switch-2-high.svg b/plugins/community/repos/DHE-Modules/res/upstage/switch-2-high.svg
new file mode 100644
index 00000000..2e1a54f0
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/upstage/switch-2-high.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/res/upstage/switch-2-low.svg b/plugins/community/repos/DHE-Modules/res/upstage/switch-2-low.svg
new file mode 100644
index 00000000..6ba49e0d
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/res/upstage/switch-2-low.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/community/repos/DHE-Modules/script/appveyor-build.sh b/plugins/community/repos/DHE-Modules/script/appveyor-build.sh
new file mode 100644
index 00000000..f4d56d0e
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/script/appveyor-build.sh
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+set -o errexit
+set -o nounset
+set -o xtrace
+
+echo "MSYSTEM=${MSYSTEM}"
+export VCV_PLUGIN_NAME=DHE-Modules
+export VCV_PLUGIN_DIR="${APPVEYOR_BUILD_FOLDER}"
+export VCV_RACK_DIR=/c/tmp/Rack
+export VCV_RACK_COMMIT=master
+export VCV_HOME_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+
+env | sort
+
+mkdir -p "${VCV_RACK_DIR}" \
+git clone -n https://github.com/VCVRack/Rack.git "${VCV_RACK_DIR}" || true
+cd "${VCV_RACK_DIR}"
+git checkout ${VCV_RACK_COMMIT}
+git pull
+git submodule update --init --recursive
+
+make dep > /dev/null
+make
+
+cd "${VCV_PLUGIN_DIR}"
+
+make clean test dist RACK_DIR="${VCV_RACK_DIR}"
diff --git a/plugins/community/repos/DHE-Modules/script/build-rack.sh b/plugins/community/repos/DHE-Modules/script/build-rack.sh
new file mode 100644
index 00000000..8a6d3f2b
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/script/build-rack.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+# This script uses these environment variables:
+#
+# VCV_RACK_DIR
+#
+# The directory in which to clone and build Rack.
+#
+# VCV_RACK_COMMIT
+#
+# Which Rack commit to build.
+
+mkdir -p "${VCV_RACK_DIR}" \
+git clone -n https://github.com/VCVRack/Rack.git "${VCV_RACK_DIR}" || true
+cd "${VCV_RACK_DIR}"
+git checkout ${VCV_RACK_COMMIT}
+git pull
+git submodule update --init --recursive
+
+make dep > /dev/null
+make
diff --git a/plugins/community/repos/DHE-Modules/src/gui/booster-stage-widget.cpp b/plugins/community/repos/DHE-Modules/src/gui/booster-stage-widget.cpp
new file mode 100644
index 00000000..87bdcfd5
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/gui/booster-stage-widget.cpp
@@ -0,0 +1,110 @@
+#include
+#include
+#include "../plugin/dhe-modules.h"
+
+#include "modules/booster-stage-module.h"
+#include "booster-stage-widget.h"
+
+namespace rack_plugin_DHE_Modules {
+
+struct BoosterStageButtonDark : rack::SVGSwitch, rack::MomentarySwitch {
+ BoosterStageButtonDark() {
+ addFrame(rack::SVG::load(rack::assetPlugin(plugin, "res/booster-stage/button-dark-off.svg")));
+ addFrame(rack::SVG::load(rack::assetPlugin(plugin, "res/booster-stage/button-dark-on.svg")));
+ }
+};
+
+struct BoosterStageButtonLight : rack::SVGSwitch, rack::MomentarySwitch {
+ BoosterStageButtonLight() {
+ addFrame(rack::SVG::load(rack::assetPlugin(plugin, "res/booster-stage/button-light-off.svg")));
+ addFrame(rack::SVG::load(rack::assetPlugin(plugin, "res/booster-stage/button-light-on.svg")));
+ }
+};
+
+struct BoosterStageKnobLarge : rack::RoundKnob {
+ BoosterStageKnobLarge() {
+ setSVG(rack::SVG::load(rack::assetPlugin(plugin, "res/booster-stage/knob-large.svg")));
+ shadow->opacity = 0.f;
+ }
+};
+
+struct BoosterStagePort : rack::SVGPort {
+ BoosterStagePort() {
+ background->svg = rack::SVG::load(assetPlugin(plugin, "res/booster-stage/port.svg"));
+ background->wrap();
+ box.size = background->box.size;
+ }
+};
+
+struct BoosterStageSwitch2 : rack::SVGSwitch, rack::ToggleSwitch {
+ BoosterStageSwitch2() {
+ addFrame(rack::SVG::load(rack::assetPlugin(plugin, "res/booster-stage/switch-2-low.svg")));
+ addFrame(rack::SVG::load(rack::assetPlugin(plugin, "res/booster-stage/switch-2-high.svg")));
+ }
+};
+
+struct BoosterStageSwitch3 : rack::SVGSwitch, rack::ToggleSwitch {
+ BoosterStageSwitch3() {
+ addFrame(rack::SVG::load(rack::assetPlugin(plugin, "res/booster-stage/switch-3-low.svg")));
+ addFrame(rack::SVG::load(rack::assetPlugin(plugin, "res/booster-stage/switch-3-mid.svg")));
+ addFrame(rack::SVG::load(rack::assetPlugin(plugin, "res/booster-stage/switch-3-high.svg")));
+ }
+};
+
+BoosterStageWidget::BoosterStageWidget(rack::Module *module) : ModuleWidget(module, 8, "res/booster-stage/panel.svg") {
+ auto width = 8.f*5.08f;
+
+ auto left_x = width/6.f + 0.3333333f;
+ auto center_x = width/2.f;
+ auto right_x = width - left_x;
+ auto button_port_distance = 7.891f;
+ auto center_left_x = left_x + button_port_distance;
+ auto center_right_x = right_x - button_port_distance;
+
+ auto top_row_y = 25.f;
+ auto row_spacing = 18.5f;
+
+ auto row = 0;
+ install_input(BoosterStageModule::LEVEL_CV, {left_x, top_row_y + row*row_spacing});
+ install_knob(BoosterStageModule::LEVEL_KNOB, {center_x, top_row_y + row*row_spacing});
+ install_switch(BoosterStageModule::LEVEL_SWITCH, {right_x, top_row_y + row*row_spacing}, 1, 1);
+
+ row++;
+ install_input(BoosterStageModule::CURVE_CV, {left_x, top_row_y + row*row_spacing});
+ install_knob(BoosterStageModule::CURVE_KNOB, {center_x, top_row_y + row*row_spacing});
+ install_switch(BoosterStageModule::SHAPE_SWITCH, {right_x, top_row_y + row*row_spacing});
+
+ row++;
+ install_input(BoosterStageModule::DURATION_CV, {left_x, top_row_y + row*row_spacing});
+ install_knob(BoosterStageModule::DURATION_KNOB, {center_x, top_row_y + row*row_spacing});
+ install_switch(BoosterStageModule::DURATION_SWITCH, {right_x, top_row_y + row*row_spacing}, 2, 1);
+
+ top_row_y = 82.f;
+ row_spacing = 15.f;
+
+ row = 0;
+ install_input(BoosterStageModule::DEFER_IN, {left_x, top_row_y + row*row_spacing});
+ install_switch(BoosterStageModule::DEFER_BUTTON, {center_left_x, top_row_y + row*row_spacing});
+ install_switch(BoosterStageModule::ACTIVE_BUTTON, {center_right_x, top_row_y + row*row_spacing});
+ install_output(BoosterStageModule::ACTIVE_OUT, {right_x, top_row_y + row*row_spacing});
+
+ row++;
+ install_input(BoosterStageModule::TRIGGER_IN, {left_x, top_row_y + row*row_spacing});
+ install_switch(BoosterStageModule::TRIGGER_BUTTON, {center_left_x, top_row_y + row*row_spacing});
+ install_switch(BoosterStageModule::EOC_BUTTON, {center_right_x, top_row_y + row*row_spacing});
+ install_output(BoosterStageModule::EOC_OUT, {right_x, top_row_y + row*row_spacing});
+
+ row++;
+ install_input(BoosterStageModule::ENVELOPE_IN, {left_x, top_row_y + row*row_spacing});
+ install_output(BoosterStageModule::ENVELOPE_OUT, {right_x, top_row_y + row*row_spacing});
+}
+
+} // namespace rack_plugin_DHE_Modules
+
+using namespace rack_plugin_DHE_Modules;
+
+
+RACK_PLUGIN_MODEL_INIT(DHE_Modules, BoosterStage) {
+ Model *modelBoosterStage = rack_plugin_DHE_Modules::createModel("Booster Stage", rack::ENVELOPE_GENERATOR_TAG);
+ return modelBoosterStage;
+}
diff --git a/plugins/community/repos/DHE-Modules/src/gui/booster-stage-widget.h b/plugins/community/repos/DHE-Modules/src/gui/booster-stage-widget.h
new file mode 100644
index 00000000..101b8ad0
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/gui/booster-stage-widget.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "module-widget.h"
+
+namespace rack_plugin_DHE_Modules {
+
+struct BoosterStageWidget : public ModuleWidget {
+ explicit BoosterStageWidget(rack::Module *module);
+};
+}
diff --git a/plugins/community/repos/DHE-Modules/src/gui/cubic-widget.cpp b/plugins/community/repos/DHE-Modules/src/gui/cubic-widget.cpp
new file mode 100644
index 00000000..e252a44e
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/gui/cubic-widget.cpp
@@ -0,0 +1,72 @@
+#include
+
+#include "plugin/dhe-modules.h"
+#include "modules/cubic-module.h"
+#include "cubic-widget.h"
+
+namespace rack_plugin_DHE_Modules {
+
+struct CubicKnobsmall : rack::RoundKnob {
+ CubicKnobsmall() {
+ setSVG(rack::SVG::load(rack::assetPlugin(plugin, "res/cubic/knob-small.svg")));
+ shadow->opacity = 0.f;
+ }
+};
+
+struct CubicPort : rack::SVGPort {
+ CubicPort() {
+ background->svg = rack::SVG::load(assetPlugin(plugin, "res/cubic/port.svg"));
+ background->wrap();
+ box.size = background->box.size;
+ }
+};
+
+CubicWidget::CubicWidget(rack::Module *module) : ModuleWidget(module, 5, "res/cubic/panel.svg") {
+ auto c_knob_rotation = rack_plugin_DHE_Modules::CubicModule::coefficient_range().normalize(1.f);
+ auto gain_knob_rotation = rack_plugin_DHE_Modules::CubicModule::gain_range().normalize((1.f));
+ auto widget_right_edge = width();
+
+ auto left_x = width()/4.f + 0.333333f;
+ auto right_x = widget_right_edge - left_x;
+
+ auto top_row_y = 20.f;
+ auto row_spacing = 15.f;
+
+ auto row = 0;
+ install_input(CubicModule::X3_CV, {left_x, top_row_y + row*row_spacing});
+ install_knob(CubicModule::X3_KNOB, {right_x, top_row_y + row*row_spacing});
+
+ row++;
+ install_input(CubicModule::X2_CV, {left_x, top_row_y + row*row_spacing});
+ install_knob(CubicModule::X2_KNOB, {right_x, top_row_y + row*row_spacing});
+
+ row++;
+ install_input(CubicModule::X1_CV, {left_x, top_row_y + row*row_spacing});
+ install_knob(CubicModule::X1_KNOB, {right_x, top_row_y + row*row_spacing}, c_knob_rotation);
+
+ row++;
+ install_input(CubicModule::X0_CV, {left_x, top_row_y + row*row_spacing});
+ install_knob(CubicModule::X0_KNOB, {right_x, top_row_y + row*row_spacing});
+
+ top_row_y = 82.f;
+ row = 0;
+ install_knob(CubicModule::INPUT_GAIN_KNOB, {left_x, top_row_y + row*row_spacing}, gain_knob_rotation);
+ install_knob(CubicModule::OUTPUT_GAIN_KNOB, {right_x, top_row_y + row*row_spacing}, gain_knob_rotation);
+
+ row++;
+ install_input(CubicModule::INPUT_GAIN_CV, {left_x, top_row_y + row*row_spacing});
+ install_input(CubicModule::OUTPUT_GAIN_CV, {right_x, top_row_y + row*row_spacing});
+
+ row++;
+ install_input(CubicModule::IN, {left_x, top_row_y + row*row_spacing});
+ install_output(CubicModule::OUT, {right_x, top_row_y + row*row_spacing});
+}
+
+} // namespace rack_plugin_DHE_Modules
+
+using namespace rack_plugin_DHE_Modules;
+
+RACK_PLUGIN_MODEL_INIT(DHE_Modules, Cubic) {
+ Model *modelCubic = rack_plugin_DHE_Modules::createModel("Cubic", rack::FUNCTION_GENERATOR_TAG);
+ return modelCubic;
+}
diff --git a/plugins/community/repos/DHE-Modules/src/gui/cubic-widget.h b/plugins/community/repos/DHE-Modules/src/gui/cubic-widget.h
new file mode 100644
index 00000000..764a38c5
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/gui/cubic-widget.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "module-widget.h"
+
+namespace rack_plugin_DHE_Modules {
+
+struct CubicWidget : public ModuleWidget {
+ explicit CubicWidget(rack::Module *module);
+};
+}
diff --git a/plugins/community/repos/DHE-Modules/src/gui/hostage-widget.cpp b/plugins/community/repos/DHE-Modules/src/gui/hostage-widget.cpp
new file mode 100644
index 00000000..ee38e8a1
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/gui/hostage-widget.cpp
@@ -0,0 +1,81 @@
+#include
+
+#include "modules/hostage-module.h"
+#include "hostage-widget.h"
+
+namespace rack_plugin_DHE_Modules {
+
+struct HostagePort : rack::SVGPort {
+ HostagePort() {
+ background->svg = rack::SVG::load(rack::assetPlugin(plugin, "res/hostage/port.svg"));
+ background->wrap();
+ box.size = background->box.size;
+ }
+};
+
+struct HostageKnobLarge : rack::RoundKnob {
+ HostageKnobLarge() {
+ setSVG(rack::SVG::load(rack::assetPlugin(plugin, "res/hostage/knob-large.svg")));
+ shadow->opacity = 0.f;
+ }
+};
+
+struct HostageSwitch2 : rack::SVGSwitch, rack::ToggleSwitch {
+ HostageSwitch2() {
+ addFrame(rack::SVG::load(rack::assetPlugin(plugin, "res/hostage/switch-2-low.svg")));
+ addFrame(rack::SVG::load(rack::assetPlugin(plugin, "res/hostage/switch-2-high.svg")));
+ }
+};
+
+struct HostageSwitch3 : rack::SVGSwitch, rack::ToggleSwitch {
+ HostageSwitch3() {
+ addFrame(rack::SVG::load(rack::assetPlugin(plugin, "res/hostage/switch-3-low.svg")));
+ addFrame(rack::SVG::load(rack::assetPlugin(plugin, "res/hostage/switch-3-mid.svg")));
+ addFrame(rack::SVG::load(rack::assetPlugin(plugin, "res/hostage/switch-3-high.svg")));
+ }
+};
+
+HostageWidget::HostageWidget(rack::Module *module) : ModuleWidget(module, 5, "res/hostage/panel.svg") {
+ auto widget_right_edge = width();
+
+ auto left_x = width()/4.f + 0.333333f;
+ auto center_x = widget_right_edge/2.f;
+ auto right_x = widget_right_edge - left_x;
+
+ auto top_row_y = 25.f;
+ auto row_spacing = 18.5f;
+
+ auto row = 0;
+ install_switch(HostageModule::GATE_MODE_SWITCH, {center_x, top_row_y + row*row_spacing});
+
+ row++;
+ install_input(HostageModule::DURATION_CV, {left_x, top_row_y + row*row_spacing});
+ install_switch(HostageModule::DURATION_SWITCH, {right_x, top_row_y + row*row_spacing}, 2, 1);
+
+ row++;
+ install_knob(HostageModule::DURATION_KNOB, {center_x, top_row_y + row*row_spacing});
+
+ top_row_y = 82.f;
+ row_spacing = 15.f;
+
+ row = 0;
+ install_input(HostageModule::DEFER_IN, {left_x, top_row_y + row*row_spacing});
+ install_output(HostageModule::ACTIVE_OUT, {right_x, top_row_y + row*row_spacing});
+
+ row++;
+ install_input(HostageModule::HOLD_GATE_IN, {left_x, top_row_y + row*row_spacing});
+ install_output(HostageModule::EOC_OUT, {right_x, top_row_y + row*row_spacing});
+
+ row++;
+ install_input(HostageModule::ENVELOPE_IN, {left_x, top_row_y + row*row_spacing});
+ install_output(HostageModule::ENVELOPE_OUT, {right_x, top_row_y + row*row_spacing});
+}
+
+} // namespace rack_plugin_DHE_Modules
+
+using namespace rack_plugin_DHE_Modules;
+
+RACK_PLUGIN_MODEL_INIT(DHE_Modules, Hostage) {
+ Model *modelHostage = rack_plugin_DHE_Modules::createModel("Hostage", rack::ENVELOPE_GENERATOR_TAG);
+ return modelHostage;
+}
diff --git a/plugins/community/repos/DHE-Modules/src/gui/hostage-widget.h b/plugins/community/repos/DHE-Modules/src/gui/hostage-widget.h
new file mode 100644
index 00000000..b8c32e20
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/gui/hostage-widget.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "module-widget.h"
+
+namespace rack_plugin_DHE_Modules {
+
+struct HostageWidget : public ModuleWidget {
+ explicit HostageWidget(rack::Module *module);
+};
+}
diff --git a/plugins/community/repos/DHE-Modules/src/gui/module-widget.cpp b/plugins/community/repos/DHE-Modules/src/gui/module-widget.cpp
new file mode 100644
index 00000000..8afe8df7
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/gui/module-widget.cpp
@@ -0,0 +1,57 @@
+#include
+#include
+
+#include
+#include
+#include
+
+#include "module-widget.h"
+
+namespace rack_plugin_DHE_Modules {
+
+ModuleWidget::ModuleWidget(rack::Module *module, int widget_hp, const char *background)
+ : rack::ModuleWidget(module) {
+ box.size = rack::Vec{(float) widget_hp*rack::RACK_GRID_WIDTH, rack::RACK_GRID_HEIGHT};
+
+ auto *panel = new rack::SVGPanel();
+ panel->box.size = box.size;
+ panel->setBackground(rack::SVG::load(rack::assetPlugin(plugin, background)));
+ addChild(panel);
+
+ install_screws();
+}
+
+void ModuleWidget::install_screws() {
+
+ auto screw_diameter = rack::RACK_GRID_WIDTH*MM_PER_IN/SVG_DPI;
+ auto screw_radius = screw_diameter/2.f;
+
+ auto top = screw_radius;
+ auto bottom = height() - top;
+
+ auto max_screw_inset = screw_diameter*1.5f;
+ auto left = std::min(width()/4.f, max_screw_inset);
+ auto right = width() - left;
+
+ auto screw_positions = std::vector{
+ {left, top},
+ {left, bottom},
+ {right, top},
+ {right, bottom}
+ };
+
+ std::shuffle(screw_positions.begin(), screw_positions.end(), std::mt19937(std::random_device()()));
+
+ install_screw(screw_positions.back());
+
+ screw_positions.pop_back();
+
+ for (auto p : screw_positions) {
+ install_screw(p);
+ }
+}
+
+void ModuleWidget::moveTo(rack::Rect &box, rack::Vec center) {
+ box.pos = center.minus(box.size.mult(0.5f));
+}
+}
diff --git a/plugins/community/repos/DHE-Modules/src/gui/module-widget.h b/plugins/community/repos/DHE-Modules/src/gui/module-widget.h
new file mode 100644
index 00000000..a3fa24f0
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/gui/module-widget.h
@@ -0,0 +1,67 @@
+#pragma once
+
+#include
+#include
+#include
+
+#include "plugin/dhe-modules.h"
+
+namespace rack_plugin_DHE_Modules {
+
+class ModuleWidget : public rack::ModuleWidget {
+
+public:
+ ModuleWidget(rack::Module *module, int widget_hp, const char *background);
+
+ float height() const {
+ return box.size.y*MM_PER_IN/SVG_DPI;
+ }
+
+ float width() const {
+ return box.size.x*MM_PER_IN/SVG_DPI;
+ }
+
+ template
+ void install_input(int index, rack::Vec center) {
+ auto input = rack::Port::create({0, 0}, rack::Port::INPUT, module, index);
+ moveTo(input->box, rack::mm2px(center));
+ addInput(input);
+ }
+
+ template
+ void install_knob(int index, rack::Vec center, float initial_rotation = 0.5f) {
+ install_param(index, center, 0.f, 1.f, initial_rotation);
+ }
+
+ template
+ void install_output(int index, rack::Vec center) {
+ auto output = rack::Port::create({0, 0}, rack::Port::OUTPUT, module, index);
+ moveTo(output->box, rack::mm2px(center));
+ addOutput(output);
+ }
+
+ template
+ void install_param(int index, rack::Vec center, float low, float high, float initial) {
+ auto param = rack::ParamWidget::create({0, 0}, module, index, low, high, initial);
+ moveTo(param->box, rack::mm2px(center));
+ addParam(param);
+ }
+
+ template
+ void install_screw(rack::Vec center) {
+ auto widget = rack::Widget::create({0, 0});
+ moveTo(widget->box, rack::mm2px(center));
+ addChild(widget);
+ }
+
+ template
+ void install_switch(int index, rack::Vec center, int max_position = 1, int initial_position = 0) {
+ install_param(index, center, 0.f, (float) max_position, (float) initial_position);
+ }
+
+ static void moveTo(rack::Rect &box, rack::Vec center);
+
+ void install_screws();
+};
+
+}
diff --git a/plugins/community/repos/DHE-Modules/src/gui/stage-widget.cpp b/plugins/community/repos/DHE-Modules/src/gui/stage-widget.cpp
new file mode 100644
index 00000000..49bd1e38
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/gui/stage-widget.cpp
@@ -0,0 +1,65 @@
+#include
+
+#include "modules/stage-module.h"
+#include "stage-widget.h"
+
+namespace rack_plugin_DHE_Modules {
+
+struct StagePort : rack::SVGPort {
+ StagePort() {
+ background->svg = rack::SVG::load(rack::assetPlugin(plugin, "res/stage/port.svg"));
+ background->wrap();
+ box.size = background->box.size;
+ }
+};
+
+struct StageKnobLarge : rack::RoundKnob {
+ StageKnobLarge() {
+ setSVG(rack::SVG::load(rack::assetPlugin(plugin, "res/stage/knob-large.svg")));
+ shadow->opacity = 0.f;
+ }
+};
+
+StageWidget::StageWidget(rack::Module *module) : ModuleWidget(module, 5, "res/stage/panel.svg") {
+ auto widget_right_edge = width();
+
+ auto left_x = width()/4.f + 0.333333f;
+ auto center_x = widget_right_edge/2.f;
+ auto right_x = widget_right_edge - left_x;
+
+ auto top_row_y = 25.f;
+ auto row_spacing = 18.5f;
+
+ auto row = 0;
+ install_knob(StageModule::LEVEL_KNOB, {center_x, top_row_y + row*row_spacing});
+
+ row++;
+ install_knob(StageModule::CURVE_KNOB, {center_x, top_row_y + row*row_spacing});
+
+ row++;
+ install_knob(StageModule::DURATION_KNOB, {center_x, top_row_y + row*row_spacing});
+
+ top_row_y = 82.f;
+ row_spacing = 15.f;
+
+ row = 0;
+ install_input(StageModule::DEFER_IN, {left_x, top_row_y + row*row_spacing});
+ install_output(StageModule::ACTIVE_OUT, {right_x, top_row_y + row*row_spacing});
+
+ row++;
+ install_input(StageModule::TRIGGER_IN, {left_x, top_row_y + row*row_spacing});
+ install_output(StageModule::EOC_OUT, {right_x, top_row_y + row*row_spacing});
+
+ row++;
+ install_input(StageModule::ENVELOPE_IN, {left_x, top_row_y + row*row_spacing});
+ install_output(StageModule::ENVELOPE_OUT, {right_x, top_row_y + row*row_spacing});
+}
+
+} // namespace rack_plugin_DHE_Modules
+
+using namespace rack_plugin_DHE_Modules;
+
+RACK_PLUGIN_MODEL_INIT(DHE_Modules, Stage) {
+ Model *modelStage = rack_plugin_DHE_Modules::createModel("Stage", rack::ENVELOPE_GENERATOR_TAG);
+ return modelStage;
+}
diff --git a/plugins/community/repos/DHE-Modules/src/gui/stage-widget.h b/plugins/community/repos/DHE-Modules/src/gui/stage-widget.h
new file mode 100644
index 00000000..b7e385fe
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/gui/stage-widget.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "module-widget.h"
+
+namespace rack_plugin_DHE_Modules {
+
+struct StageWidget : public ModuleWidget {
+ explicit StageWidget(rack::Module *module);
+};
+}
diff --git a/plugins/community/repos/DHE-Modules/src/gui/swave-widget.cpp b/plugins/community/repos/DHE-Modules/src/gui/swave-widget.cpp
new file mode 100644
index 00000000..a7174bd0
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/gui/swave-widget.cpp
@@ -0,0 +1,67 @@
+#include
+
+#include "plugin/dhe-modules.h"
+#include "modules/swave-module.h"
+#include "swave-widget.h"
+
+namespace rack_plugin_DHE_Modules {
+
+struct SwaveKnobLarge : rack::RoundKnob {
+ SwaveKnobLarge() {
+ setSVG(rack::SVG::load(rack::assetPlugin(plugin, "res/swave/knob-large.svg")));
+ shadow->opacity = 0.f;
+ }
+};
+
+struct SwavePort : rack::SVGPort {
+ SwavePort() {
+ background->svg = rack::SVG::load(assetPlugin(plugin, "res/swave/port.svg"));
+ background->wrap();
+ box.size = background->box.size;
+ }
+};
+
+struct SwaveSwitch2 : rack::SVGSwitch, rack::ToggleSwitch {
+ SwaveSwitch2() {
+ addFrame(rack::SVG::load(rack::assetPlugin(plugin, "res/swave/switch-2-low.svg")));
+ addFrame(rack::SVG::load(rack::assetPlugin(plugin, "res/swave/switch-2-high.svg")));
+ }
+};
+
+SwaveWidget::SwaveWidget(rack::Module *module) : ModuleWidget(module, 4, "res/swave/panel.svg") {
+ auto widget_right_edge = width();
+
+ auto center_x = widget_right_edge/2.f;
+
+ auto top_row_y = 25.f;
+ auto row_spacing = 18.5f;
+
+ auto row = 0;
+ install_switch(SwaveModule::SHAPE_SWITCH, {center_x, top_row_y + row*row_spacing}, 1, 1);
+
+ row++;
+ install_knob(SwaveModule::CURVE_KNOB, {center_x, top_row_y + row*row_spacing});
+
+ row++;
+ install_input(SwaveModule::CURVE_CV, {center_x, top_row_y + row*row_spacing});
+
+ top_row_y = 82.f;
+ row_spacing = 15.f;
+
+ row = 0;
+
+ row++;
+ install_input(SwaveModule::SWAVE_IN, {center_x, top_row_y + row*row_spacing});
+
+ row++;
+ install_output(SwaveModule::SWAVE_OUT, {center_x, top_row_y + row*row_spacing});
+}
+
+} // namespace rack_plugin_DHE_Modules
+
+using namespace rack_plugin_DHE_Modules;
+
+RACK_PLUGIN_MODEL_INIT(DHE_Modules, Swave) {
+ Model *modelSwave = rack_plugin_DHE_Modules::createModel("Swave", rack::WAVESHAPER_TAG);
+ return modelSwave;
+}
diff --git a/plugins/community/repos/DHE-Modules/src/gui/swave-widget.h b/plugins/community/repos/DHE-Modules/src/gui/swave-widget.h
new file mode 100644
index 00000000..22ae7863
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/gui/swave-widget.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "module-widget.h"
+
+namespace rack_plugin_DHE_Modules {
+
+struct SwaveWidget : public ModuleWidget {
+ explicit SwaveWidget(rack::Module *module);
+};
+}
diff --git a/plugins/community/repos/DHE-Modules/src/gui/upstage-widget.cpp b/plugins/community/repos/DHE-Modules/src/gui/upstage-widget.cpp
new file mode 100644
index 00000000..f70e17b5
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/gui/upstage-widget.cpp
@@ -0,0 +1,80 @@
+#include
+#include
+
+#include "modules/upstage-module.h"
+#include "upstage-widget.h"
+
+namespace rack_plugin_DHE_Modules {
+
+struct UpstageButtonDark : rack::SVGSwitch, rack::MomentarySwitch {
+ UpstageButtonDark() {
+ addFrame(rack::SVG::load(rack::assetPlugin(plugin, "res/upstage/button-dark-off.svg")));
+ addFrame(rack::SVG::load(rack::assetPlugin(plugin, "res/upstage/button-dark-on.svg")));
+ }
+};
+
+struct UpstageKnobLarge : rack::RoundKnob {
+ UpstageKnobLarge() {
+ setSVG(rack::SVG::load(rack::assetPlugin(plugin, "res/upstage/knob-large.svg")));
+ shadow->opacity = 0.f;
+ }
+};
+
+struct UpstagePort : rack::SVGPort {
+ UpstagePort() {
+ background->svg = rack::SVG::load(assetPlugin(plugin, "res/upstage/port.svg"));
+ background->wrap();
+ box.size = background->box.size;
+ }
+};
+
+struct UpstageSwitch2 : rack::SVGSwitch, rack::ToggleSwitch {
+ UpstageSwitch2() {
+ addFrame(rack::SVG::load(rack::assetPlugin(plugin, "res/upstage/switch-2-low.svg")));
+ addFrame(rack::SVG::load(rack::assetPlugin(plugin, "res/upstage/switch-2-high.svg")));
+ }
+};
+
+UpstageWidget::UpstageWidget(rack::Module *module) : ModuleWidget(module, 5, "res/upstage/panel.svg") {
+ auto widget_right_edge = width();
+
+ auto left_x = width()/4.f + 0.333333333f;
+ auto center_x = widget_right_edge/2.f;
+ auto right_x = widget_right_edge - left_x;
+
+ auto top_row_y = 25.f;
+ auto row_spacing = 18.5f;
+
+ auto row = 0;
+ install_knob(UpstageModule::LEVEL_KNOB, {center_x, top_row_y + row*row_spacing});
+
+ row++;
+ install_input(UpstageModule::LEVEL_CV, {left_x, top_row_y + row*row_spacing});
+ install_switch(UpstageModule::LEVEL_SWITCH, {right_x, top_row_y + row*row_spacing}, 1, 1);
+
+ row++;
+ install_switch(UpstageModule::WAIT_BUTTON, {left_x, top_row_y + row*row_spacing});
+ install_switch(UpstageModule::TRIG_BUTTON, {right_x, top_row_y + row*row_spacing});
+
+ top_row_y = 82.f;
+ row_spacing = 15.f;
+
+ row = 0;
+ install_input(UpstageModule::WAIT_IN, {left_x, top_row_y + row*row_spacing});
+
+ row++;
+ install_input(UpstageModule::TRIG_IN, {left_x, top_row_y + row*row_spacing});
+ install_output(UpstageModule::TRIG_OUT, {right_x, top_row_y + row*row_spacing});
+
+ row++;
+ install_output(UpstageModule::ENVELOPE_OUT, {right_x, top_row_y + row*row_spacing});
+}
+
+} // namespace rack_plugin_DHE_Modules
+
+using namespace rack_plugin_DHE_Modules;
+
+RACK_PLUGIN_MODEL_INIT(DHE_Modules, Upstage) {
+ Model *modelUpstage = rack_plugin_DHE_Modules::createModel("Upstage", rack::ENVELOPE_GENERATOR_TAG);
+ return modelUpstage;
+}
diff --git a/plugins/community/repos/DHE-Modules/src/gui/upstage-widget.h b/plugins/community/repos/DHE-Modules/src/gui/upstage-widget.h
new file mode 100644
index 00000000..ec100ee9
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/gui/upstage-widget.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "module-widget.h"
+
+namespace rack_plugin_DHE_Modules {
+
+struct UpstageWidget : public ModuleWidget {
+ explicit UpstageWidget(rack::Module *module);
+};
+}
diff --git a/plugins/community/repos/DHE-Modules/src/modules/booster-stage-module.h b/plugins/community/repos/DHE-Modules/src/modules/booster-stage-module.h
new file mode 100644
index 00000000..2a886a76
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/modules/booster-stage-module.h
@@ -0,0 +1,170 @@
+#pragma once
+
+#include
+#include
+#include
+
+#include
+#include "util/controls.h"
+#include "module.h"
+
+namespace rack_plugin_DHE_Modules {
+
+#define sMIN(a,b) (((a)>(b))?(b):(a))
+#define sMAX(a,b) (((a)>(b))?(a):(b))
+
+struct BoosterStageModule : Module {
+ Mode stage_mode = {};
+ Mode defer_mode = {};
+
+ // TODO: Move this inside stage mode or an envelope class.
+ float phase_0_voltage{0.f};
+ bool is_active{false};
+ bool is_eoc{false};
+
+ Ramp envelope = Ramp{[this] { return sample_time()/duration_in(); }};
+ Ramp eoc_pulse = Ramp{[this] { return sample_time()/1e-3f; }};
+ DFlipFlop envelope_trigger = DFlipFlop{[this] { return envelope_gate_in(); }};
+
+ SubmodeSwitch executor = {[this] { return defer_gate_in(); }, &stage_mode, &defer_mode};
+
+ BoosterStageModule()
+ : Module{PARAMETER_COUNT, INPUT_COUNT, OUTPUT_COUNT} {
+
+ defer_mode.on_entry([this] {
+ is_active = true;
+ });
+ defer_mode.on_step([this] {
+ send_envelope(envelope_in());
+ });
+
+ stage_mode.on_entry([this] {
+ is_active = false;
+ phase_0_voltage = envelope_in();
+ envelope_trigger.enable();
+ });
+ stage_mode.on_step([this] {
+ envelope_trigger.step();
+ envelope.step();
+ send_envelope(envelope_voltage(envelope.phase())); // TODO: Move to envelope.on_step()
+ });
+ stage_mode.on_exit([this] {
+ envelope_trigger.disable();
+ envelope.stop();
+ });
+
+ envelope_trigger.on_rise([this] {
+ phase_0_voltage = envelope_in();
+ envelope.start();
+ });
+
+ envelope.on_start([this] {
+ is_active = true;
+ });
+ envelope.on_completion([this] {
+ is_active = false;
+ eoc_pulse.start();
+ });
+
+ eoc_pulse.on_start([this] {
+ is_eoc = true;
+ });
+ eoc_pulse.on_completion([this] {
+ is_eoc = false;
+ });
+
+ executor.on_step([this] { eoc_pulse.step(); });
+ executor.enter();
+ }
+
+ void step() override {
+ executor.step();
+ send_active();
+ send_eoc();
+ }
+
+ float defer_gate_in() const {
+ return sMAX(input(DEFER_IN), gate_button(DEFER_BUTTON));
+ }
+
+ float duration_in() const {
+ auto rotation = modulated(DURATION_KNOB, DURATION_CV);
+ const auto &range = Duration::range(param(DURATION_SWITCH));
+ return Duration::scaled(rotation, range);
+ }
+
+ float envelope_gate_in() const {
+ return sMAX(input(TRIGGER_IN), gate_button(TRIGGER_BUTTON));
+ }
+
+ float envelope_in() const {
+ return input(ENVELOPE_IN);
+ }
+
+ float envelope_voltage(float phase) const {
+ return scale(taper(phase), phase_0_voltage, level_in());
+ }
+
+ bool is_s_taper() const {
+ return param(SHAPE_SWITCH) > 0.5;
+ }
+
+ float level_in() const {
+ const auto &range = Level::range(param(LEVEL_SWITCH));
+ auto rotation = modulated(LEVEL_KNOB, LEVEL_CV);
+ return Level::scaled(rotation, range);
+ }
+
+ void send_active() {
+ outputs[ACTIVE_OUT].value = UNIPOLAR_SIGNAL_RANGE.scale(is_active || param(ACTIVE_BUTTON) > 0.5f);
+ }
+
+ void send_envelope(float voltage) {
+ outputs[ENVELOPE_OUT].value = voltage;
+ }
+
+ void send_eoc() {
+ outputs[EOC_OUT].value = UNIPOLAR_SIGNAL_RANGE.scale(is_eoc || param(EOC_BUTTON) > 0.5f);
+ }
+
+ float sample_time() const {
+ return rack::engineGetSampleTime();
+ }
+
+ float taper(float phase) const {
+ auto rotation = modulated(CURVE_KNOB, CURVE_CV);
+ return is_s_taper() ? Taper::s(phase, rotation) : Taper::j(phase, rotation);
+ }
+
+ enum ParameterIds {
+ ACTIVE_BUTTON,
+ CURVE_KNOB,
+ DEFER_BUTTON,
+ DURATION_KNOB,
+ DURATION_SWITCH,
+ EOC_BUTTON,
+ LEVEL_KNOB,
+ LEVEL_SWITCH,
+ SHAPE_SWITCH,
+ TRIGGER_BUTTON,
+ PARAMETER_COUNT
+ };
+
+ enum InputIds {
+ CURVE_CV,
+ DEFER_IN,
+ DURATION_CV,
+ LEVEL_CV,
+ ENVELOPE_IN,
+ TRIGGER_IN,
+ INPUT_COUNT
+ };
+
+ enum OutputIds {
+ ACTIVE_OUT,
+ EOC_OUT,
+ ENVELOPE_OUT,
+ OUTPUT_COUNT
+ };
+};
+}
diff --git a/plugins/community/repos/DHE-Modules/src/modules/cubic-module.h b/plugins/community/repos/DHE-Modules/src/modules/cubic-module.h
new file mode 100644
index 00000000..5ce0a333
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/modules/cubic-module.h
@@ -0,0 +1,71 @@
+#pragma once
+
+#include "module.h"
+
+namespace rack_plugin_DHE_Modules {
+
+struct CubicModule : Module {
+ CubicModule() : Module{PARAMETER_COUNT, INPUT_COUNT, OUTPUT_COUNT} {
+ }
+
+ static const Range &gain_range() {
+ static constexpr auto gain_range = Range{0.f, 2.f};
+ return gain_range;
+ }
+
+ static const Range &coefficient_range() {
+ static constexpr auto coefficient_range = Range{-2.f, 2.f};
+ return coefficient_range;
+ }
+
+ float gain(int knob, int cv) const {
+ return gain_range().scale(modulated(knob, cv));
+ }
+
+ float coefficient(int knob, int cv) const {
+ return coefficient_range().scale(modulated(knob, cv));
+ }
+
+ float a() const { return coefficient(X3_KNOB, X3_CV); }
+ float b() const { return coefficient(X2_KNOB, X2_CV); }
+ float c() const { return coefficient(X1_KNOB, X1_CV); }
+ float d() const { return coefficient(X0_KNOB, X0_CV); }
+ float input_gain() const { return gain(INPUT_GAIN_KNOB, INPUT_GAIN_CV); }
+ float output_gain() const { return gain(OUTPUT_GAIN_KNOB, OUTPUT_GAIN_CV); }
+
+ void step() override {
+ auto x = input_gain()*input(IN)/5.f;
+ auto x2 = x*x;
+ auto x3 = x2*x;
+
+ auto y = output_gain()*(a()*x3 + b()*x2 + c()*x + d());
+
+ outputs[OUT].value = 5.f*y;
+ }
+
+ enum ParameterIds {
+ X3_KNOB,
+ X2_KNOB,
+ X1_KNOB,
+ X0_KNOB,
+ INPUT_GAIN_KNOB,
+ OUTPUT_GAIN_KNOB,
+ PARAMETER_COUNT
+ };
+ enum InputIds {
+ IN,
+ X3_CV,
+ X2_CV,
+ X1_CV,
+ X0_CV,
+ INPUT_GAIN_CV,
+ OUTPUT_GAIN_CV,
+ INPUT_COUNT
+ };
+ enum OutputIds {
+ OUT,
+ OUTPUT_COUNT
+ };
+};
+
+}
diff --git a/plugins/community/repos/DHE-Modules/src/modules/hostage-module.h b/plugins/community/repos/DHE-Modules/src/modules/hostage-module.h
new file mode 100644
index 00000000..7164a6a4
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/modules/hostage-module.h
@@ -0,0 +1,161 @@
+#pragma once
+
+#include
+#include
+
+#include "module.h"
+#include "util/controls.h"
+#include "util/d-flip-flop.h"
+#include "util/ramp.h"
+
+namespace rack_plugin_DHE_Modules {
+
+struct HostageModule : Module {
+
+ DFlipFlop sustain_gate = DFlipFlop{[this] { return hold_gate_in(); }};
+ DFlipFlop sustain_trigger = DFlipFlop{[this] { return hold_gate_in(); }};
+ Ramp timer = Ramp{[this] { return sample_time()/duration_in(); }};
+ Ramp eoc_pulse = Ramp{[this] { return sample_time()/1e-3f; }};
+
+ Mode defer_mode = {};
+ Mode timed_sustain_mode = {};
+ Mode gated_sustain_mode = {};
+ SubmodeSwitch sustain_mode = {[this] { return mode_switch_in(); }, &timed_sustain_mode, &gated_sustain_mode};
+ SubmodeSwitch executor = {[this] { return defer_gate_in(); }, &sustain_mode, &defer_mode};
+
+ HostageModule() : Module{PARAMETER_COUNT, INPUT_COUNT, OUTPUT_COUNT} {
+ defer_mode.on_entry([this] {
+ send_active(true);
+ });
+ defer_mode.on_step([this] {
+ send_envelope(envelope_in());
+ });
+
+ timed_sustain_mode.on_entry([this] {
+ sustain_trigger.enable();
+ send_active(false);
+ send_envelope(envelope_in());
+ });
+ timed_sustain_mode.on_step([this] {
+ sustain_trigger.step();
+ timer.step();
+ });
+ timed_sustain_mode.on_exit([this] {
+ sustain_trigger.disable();
+ timer.stop();
+ });
+
+ sustain_trigger.on_rise([this] {
+ timer.start();
+ });
+
+ timer.on_start([this] {
+ begin_sustaining();
+ });
+ timer.on_completion([this] {
+ end_sustaining();
+ });
+
+ gated_sustain_mode.on_entry([this] {
+ sustain_gate.step();
+ sustain_gate.enable();
+ if (sustain_gate.is_high()) begin_sustaining();
+ else end_sustaining();
+ });
+ gated_sustain_mode.on_step([this] {
+ sustain_gate.step();
+ });
+ gated_sustain_mode.on_exit([this] {
+ sustain_gate.disable();
+ });
+
+ sustain_gate.on_rise([this] {
+ begin_sustaining();
+ });
+ sustain_gate.on_fall([this] {
+ end_sustaining();
+ });
+
+ eoc_pulse.on_start([this] {
+ send_eoc(true);
+ });
+ eoc_pulse.on_completion([this] {
+ send_eoc(false);
+ });
+
+ executor.on_step([this] { eoc_pulse.step(); });
+ executor.enter();
+ }
+
+ void end_sustaining() {
+ send_active(false);
+ eoc_pulse.start();
+ }
+ void begin_sustaining() { send_active(true); }
+
+ void step() override {
+ executor.step();
+ }
+
+ float defer_gate_in() const {
+ return input(DEFER_IN);
+ };
+
+ float mode_switch_in() const {
+ return param(GATE_MODE_SWITCH);
+ }
+
+ float duration_in() const {
+ auto rotation = modulated(DURATION_KNOB, DURATION_CV);
+ const auto &range = Duration::range(param(DURATION_SWITCH));
+ return Duration::scaled(rotation, range);
+ }
+
+ float hold_gate_in() const {
+ return input(HOLD_GATE_IN);
+ }
+
+ float envelope_in() const {
+ return input(ENVELOPE_IN);
+ }
+
+ void send_active(bool is_active) {
+ outputs[ACTIVE_OUT].value = UNIPOLAR_SIGNAL_RANGE.scale(is_active);
+ }
+
+ void send_envelope(float voltage) {
+ outputs[ENVELOPE_OUT].value = voltage;
+ }
+
+ void send_eoc(bool is_pulsing) {
+ outputs[EOC_OUT].value = UNIPOLAR_SIGNAL_RANGE.scale(is_pulsing);
+ }
+
+ float sample_time() const {
+ return rack::engineGetSampleTime();
+ }
+
+ enum InputIds {
+ DEFER_IN,
+ DURATION_CV,
+ ENVELOPE_IN,
+ HOLD_GATE_IN,
+ INPUT_COUNT
+ };
+
+ enum OutputIds {
+ ACTIVE_OUT,
+ ENVELOPE_OUT,
+ EOC_OUT,
+ OUTPUT_COUNT
+ };
+
+ enum ParameterIds {
+ DURATION_KNOB,
+ DURATION_SWITCH,
+ GATE_MODE_SWITCH,
+ PARAMETER_COUNT
+ };
+};
+
+}
diff --git a/plugins/community/repos/DHE-Modules/src/modules/module.h b/plugins/community/repos/DHE-Modules/src/modules/module.h
new file mode 100644
index 00000000..32899cae
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/modules/module.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include
+
+#include "util/range.h"
+
+namespace rack_plugin_DHE_Modules {
+
+struct Module : rack::Module {
+ Module(int param_count, int input_count, int output_count)
+ : rack::Module(param_count, input_count, output_count) {}
+
+ float gate_button(int index) const {
+ return UNIPOLAR_SIGNAL_RANGE.scale(param(index) > 0.5f);
+ };
+
+ float input(int index) const {
+ return inputs[index].value;
+ }
+
+ float modulated(int param_index, int mod_index) const {
+ return param(param_index) + input(mod_index)/10.f;
+ }
+
+ float param(int index) const {
+ return params[index].value;
+ }
+};
+
+}
diff --git a/plugins/community/repos/DHE-Modules/src/modules/stage-module.h b/plugins/community/repos/DHE-Modules/src/modules/stage-module.h
new file mode 100644
index 00000000..8854f004
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/modules/stage-module.h
@@ -0,0 +1,146 @@
+#pragma once
+
+#include
+
+#include "module.h"
+#include "util/controls.h"
+#include "util/d-flip-flop.h"
+#include "util/mode.h"
+#include "util/ramp.h"
+#include "util/range.h"
+
+namespace rack_plugin_DHE_Modules {
+
+struct StageModule : public Module {
+ Mode stage_mode = {};
+ Mode defer_mode = {};
+
+ // TODO: Move this inside stage mode or an envelope class.
+ float phase_0_voltage{0.f};
+
+ Ramp envelope = Ramp{[this] { return sample_time()/duration_in(); }};
+ DFlipFlop envelope_trigger = DFlipFlop{[this] { return envelope_gate_in(); }};
+ Ramp eoc_pulse = Ramp{[this] { return sample_time()/1e-3f; }};
+
+ SubmodeSwitch executor = {[this] { return defer_gate_in(); }, &stage_mode, &defer_mode};
+
+ void onReset() override {
+ Module::onReset();
+ }
+ StageModule() : Module(PARAMETER_COUNT, INPUT_COUNT, OUTPUT_COUNT) {
+ defer_mode.on_entry([this] {
+ send_active(true);
+ });
+ defer_mode.on_step([this] {
+ send_envelope(envelope_in());
+ });
+
+ stage_mode.on_entry([this] {
+ send_active(false);
+ phase_0_voltage = envelope_in();
+ envelope_trigger.enable();
+ });
+ stage_mode.on_step([this] {
+ envelope_trigger.step();
+ envelope.step();
+ send_envelope(envelope_voltage(envelope.phase())); // TODO: Move to envelope.on_step()
+ });
+ stage_mode.on_exit([this] {
+ envelope_trigger.disable();
+ envelope.stop();
+ });
+
+ envelope_trigger.on_rise([this] {
+ phase_0_voltage = envelope_in();
+ envelope.start();
+ });
+
+ envelope.on_start([this] {
+ send_active(true);
+ });
+ envelope.on_completion([this] {
+ send_active(false);
+ eoc_pulse.start();
+ });
+
+ eoc_pulse.on_start([this] {
+ send_eoc(true);
+ });
+ eoc_pulse.on_completion([this] {
+ send_eoc(false);
+ });
+
+ executor.on_step([this] { eoc_pulse.step(); });
+ executor.enter();
+ }
+
+ float defer_gate_in() const {
+ return input(DEFER_IN);
+ }
+
+ float duration_in() const {
+ return Duration::scaled(param(DURATION_KNOB), Duration::MEDIUM_RANGE);
+ }
+
+ float envelope_gate_in() const {
+ return input(TRIGGER_IN);
+ }
+
+ float envelope_in() const {
+ return input(ENVELOPE_IN);
+ }
+
+ float envelope_voltage(float phase) const {
+ return scale(taper(phase), phase_0_voltage, level_in());
+ }
+
+ float level_in() const {
+ return Level::scaled(param(LEVEL_KNOB));
+ }
+
+ float sample_time() const {
+ return rack::engineGetSampleTime();
+ }
+
+ void send_active(bool is_active) {
+ outputs[ACTIVE_OUT].value = UNIPOLAR_SIGNAL_RANGE.scale(is_active);
+ }
+
+ void send_envelope(float voltage) {
+ outputs[ENVELOPE_OUT].value = voltage;
+ }
+
+ void send_eoc(bool is_pulsing) {
+ outputs[EOC_OUT].value = UNIPOLAR_SIGNAL_RANGE.scale(is_pulsing);
+ }
+
+ void step() override {
+ executor.step();
+ }
+
+ float taper(float phase) const {
+ return Taper::j(phase, param(CURVE_KNOB));
+ }
+
+ enum ParameterIIds {
+ DURATION_KNOB,
+ LEVEL_KNOB,
+ CURVE_KNOB,
+ PARAMETER_COUNT
+ };
+
+ enum InputIds {
+ ENVELOPE_IN,
+ TRIGGER_IN,
+ DEFER_IN,
+ INPUT_COUNT
+ };
+
+ enum OutputIds {
+ ENVELOPE_OUT,
+ EOC_OUT,
+ ACTIVE_OUT,
+ OUTPUT_COUNT
+ };
+};
+}
diff --git a/plugins/community/repos/DHE-Modules/src/modules/swave-module.h b/plugins/community/repos/DHE-Modules/src/modules/swave-module.h
new file mode 100644
index 00000000..c13f1d6a
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/modules/swave-module.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include
+
+#include
+
+#include "module.h"
+#include "util/controls.h"
+#include "util/range.h"
+
+namespace rack_plugin_DHE_Modules {
+
+struct SwaveModule : Module {
+ SwaveModule() : Module{PARAMETER_COUNT, INPUT_COUNT, OUTPUT_COUNT} {}
+
+ bool is_s_taper() const {
+ return param(SHAPE_SWITCH) > 0.5f;
+ }
+
+ void step() override {
+ outputs[SWAVE_OUT].value = to_signal(taper(to_phase(swave_in())));
+ }
+
+ float swave_in() const { return input(SWAVE_IN); }
+
+ float taper(float phase) const {
+ auto rotation = modulated(CURVE_KNOB, CURVE_CV);
+ return is_s_taper() ? Taper::s(phase, rotation) : Taper::j(phase, rotation);
+ }
+
+ float to_signal(float phase) const {
+ return BIPOLAR_SIGNAL_RANGE.scale(phase);
+ }
+
+ float to_phase(float signal) const {
+ return BIPOLAR_SIGNAL_RANGE.normalize(signal);
+ }
+
+ enum ParameterIds {
+ CURVE_KNOB,
+ SHAPE_SWITCH,
+ PARAMETER_COUNT
+ };
+ enum InputIds {
+ CURVE_CV,
+ SWAVE_IN,
+ INPUT_COUNT
+ };
+ enum OutputIds {
+ SWAVE_OUT,
+ OUTPUT_COUNT
+ };
+};
+
+}
diff --git a/plugins/community/repos/DHE-Modules/src/modules/upstage-module.h b/plugins/community/repos/DHE-Modules/src/modules/upstage-module.h
new file mode 100644
index 00000000..f752c007
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/modules/upstage-module.h
@@ -0,0 +1,57 @@
+#pragma once
+
+#include
+
+#include "module.h"
+#include "util/controls.h"
+#include "util/range.h"
+
+namespace rack_plugin_DHE_Modules {
+
+struct UpstageModule : Module {
+ UpstageModule() : Module{PARAMETER_COUNT, INPUT_COUNT, OUTPUT_COUNT} {}
+
+ float envelope_out() const {
+ const auto &range = Level::range(params[LEVEL_SWITCH].value);
+ auto rotation = modulated(LEVEL_KNOB, LEVEL_CV);
+ return Level::scaled(rotation, range);
+ }
+
+ bool is_waiting() const {
+ return std::max(inputs[WAIT_IN].value, gate_button(WAIT_BUTTON)) > 0.5f;
+ }
+
+ void step() override {
+ outputs[TRIG_OUT].value = trigger_out();
+ outputs[ENVELOPE_OUT].value = envelope_out();
+ }
+
+ float trigger_in() const {
+ return param(TRIG_BUTTON) > 0.5f ? 10.f : input(TRIG_IN);
+ }
+
+ float trigger_out() const {
+ return is_waiting() ? 0.f : trigger_in();
+ }
+
+ enum ParameterIds {
+ LEVEL_KNOB,
+ TRIG_BUTTON,
+ WAIT_BUTTON,
+ LEVEL_SWITCH,
+ PARAMETER_COUNT
+ };
+ enum InputIds {
+ TRIG_IN,
+ WAIT_IN,
+ LEVEL_CV,
+ INPUT_COUNT
+ };
+ enum OutputIds {
+ TRIG_OUT,
+ ENVELOPE_OUT,
+ OUTPUT_COUNT
+ };
+};
+
+}
diff --git a/plugins/community/repos/DHE-Modules/src/plugin/dhe-modules.cpp b/plugins/community/repos/DHE-Modules/src/plugin/dhe-modules.cpp
new file mode 100644
index 00000000..1baf416b
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/plugin/dhe-modules.cpp
@@ -0,0 +1,21 @@
+#include "dhe-modules.h"
+
+RACK_PLUGIN_MODEL_DECLARE(DHE_Modules, BoosterStage);
+RACK_PLUGIN_MODEL_DECLARE(DHE_Modules, Cubic);
+RACK_PLUGIN_MODEL_DECLARE(DHE_Modules, Hostage);
+RACK_PLUGIN_MODEL_DECLARE(DHE_Modules, Stage);
+RACK_PLUGIN_MODEL_DECLARE(DHE_Modules, Swave);
+RACK_PLUGIN_MODEL_DECLARE(DHE_Modules, Upstage);
+
+RACK_PLUGIN_INIT(DHE_Modules) {
+ RACK_PLUGIN_INIT_ID();
+
+ RACK_PLUGIN_INIT_WEBSITE("https://dhemery.github.io/DHE-Modules/");
+
+ RACK_PLUGIN_MODEL_ADD(DHE_Modules, BoosterStage);
+ RACK_PLUGIN_MODEL_ADD(DHE_Modules, Cubic);
+ RACK_PLUGIN_MODEL_ADD(DHE_Modules, Hostage);
+ RACK_PLUGIN_MODEL_ADD(DHE_Modules, Stage);
+ RACK_PLUGIN_MODEL_ADD(DHE_Modules, Swave);
+ RACK_PLUGIN_MODEL_ADD(DHE_Modules, Upstage);
+}
diff --git a/plugins/community/repos/DHE-Modules/src/plugin/dhe-modules.h b/plugins/community/repos/DHE-Modules/src/plugin/dhe-modules.h
new file mode 100644
index 00000000..49cc7eaa
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/plugin/dhe-modules.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include
+
+using namespace rack;
+
+RACK_PLUGIN_DECLARE(DHE_Modules);
+
+#ifdef USE_VST2
+#define plugin "DHE-Modules"
+#endif // USE_VST2
+
+namespace rack_plugin_DHE_Modules {
+template
+static rack::Model *createModel(std::string moduleSlug, TTag... tags) {
+ return rack::Model::create("DHE-Modules", moduleSlug, moduleSlug, tags...);
+}
+}
diff --git a/plugins/community/repos/DHE-Modules/src/util/controls.h b/plugins/community/repos/DHE-Modules/src/util/controls.h
new file mode 100644
index 00000000..30517df7
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/util/controls.h
@@ -0,0 +1,79 @@
+#pragma once
+
+#include "range.h"
+#include "sigmoid.h"
+
+namespace rack_plugin_DHE_Modules {
+
+namespace Duration {
+constexpr auto RANGE_MAX_TO_MIN_RATIO = 1000.f;
+constexpr auto MEDIUM_RANGE_MAX = 10.0f;
+constexpr auto MEDIUM_RANGE_MIN = MEDIUM_RANGE_MAX/RANGE_MAX_TO_MIN_RATIO;
+
+constexpr auto SCALE_STEP = 10.f;
+constexpr auto SHORT_RANGE = Range{MEDIUM_RANGE_MIN/SCALE_STEP, MEDIUM_RANGE_MAX/SCALE_STEP};
+constexpr auto MEDIUM_RANGE = Range{MEDIUM_RANGE_MIN, MEDIUM_RANGE_MAX};
+constexpr auto LONG_RANGE = Range{MEDIUM_RANGE_MIN*SCALE_STEP, MEDIUM_RANGE_MAX*SCALE_STEP};
+
+inline const Range &range(float switch_value) {
+ return switch_value < 0.5f ? SHORT_RANGE :
+ switch_value < 1.5f ? MEDIUM_RANGE : LONG_RANGE;
+}
+
+inline float scaled(float rotation, const Range &range) {
+ // Shapes the J taper to yield ~0.1 given a rotation of 0.5. So if the
+ // rotation is at dead center, the return value will be ~1/10 of the range's
+ // maximum.
+ constexpr auto KNOB_CURVATURE = 0.8f;
+
+ // Assuming a rotation value in the range [0,1], sigmoid() yields a J taper
+ // in the range [0,1]
+ auto j_tapered_rotation = sigmoid(rotation, KNOB_CURVATURE);
+
+ // Scale the tapered rotation to the desired range.
+ return range.scale(j_tapered_rotation);
+}
+}
+
+namespace Level {
+
+inline const Range &range(float switch_value) {
+ return switch_value > 0.5f ? UNIPOLAR_SIGNAL_RANGE : BIPOLAR_SIGNAL_RANGE;
+}
+
+inline float scaled(float rotation, const Range &range = UNIPOLAR_SIGNAL_RANGE) {
+ return range.scale(rotation);
+}
+}
+
+namespace Taper {
+
+inline float curvature(float rotation) {
+ // Scale the rotation to [-1,1] to use the entire range of the sigmoid curve.
+ auto bipolar_rotation = BIPOLAR_PHASE_RANGE.scale(rotation);
+
+ // This knob curvature gives an S taper that gently increases sensitivity in
+ // the middle of the rotation and decreases sensitivity toward the ends.
+ constexpr auto KNOB_CURVATURE = -0.65f;
+
+ // Apply the S taper to the bipolar rotation.
+ return sigmoid(bipolar_rotation, KNOB_CURVATURE);
+}
+
+inline float j(float phase, float rotation) {
+ return sigmoid(phase, curvature(rotation));
+}
+
+inline float s(float phase, float rotation) {
+ // Scale the phase to [-1,1] to use the entire range of the sigmoid curve.
+ auto bipolar_phase = BIPOLAR_PHASE_RANGE.scale(phase);
+
+ // Invert the curvature so that rotation greater than 0.5 gives an S taper.
+ auto s_tapered_bipolar_phase = sigmoid(bipolar_phase, -curvature(rotation));
+
+ // Scale the tapered phase back to the range [0,1].
+ return BIPOLAR_PHASE_RANGE.normalize(s_tapered_bipolar_phase);
+}
+
+}
+}
diff --git a/plugins/community/repos/DHE-Modules/src/util/d-flip-flop.h b/plugins/community/repos/DHE-Modules/src/util/d-flip-flop.h
new file mode 100644
index 00000000..2cbe9c2c
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/util/d-flip-flop.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+
+#include "latch.h"
+
+namespace rack_plugin_DHE_Modules {
+
+/**
+ * A flip-flop is a latch determines its own state by comparing a signal to the
+ * specified thresholds.
+ */
+struct DFlipFlop : public Latch {
+ /**
+ * Creates a flip-flop that compares the signal to the given thresholds.
+ *
+ * @param signal supplies the signal to evaluate
+ * @param low_threshold signal value at or below which the flip-flop state is LOW
+ * @param high_threshold signal value at or above which the flip-flop state is HIGH
+ */
+ DFlipFlop(std::function signal, float low_threshold, float high_threshold)
+ : signal{std::move(signal)},
+ low_threshold{low_threshold},
+ high_threshold{high_threshold} {}
+
+ /**
+ * Creates a flip-flop that compares the signal to a low threshold
+ * of 0.1 and a high threshold of 0.9.
+ *
+ * @param signal supplies the signal to evaluate
+ */
+ explicit DFlipFlop(std::function signal)
+ : signal{std::move(signal)} {}
+
+ /**
+ * Sets the state by comparing the signal to the thresholds.
+ * - Fires 'rise' if the state changes to HIGH.
+ * - Fires 'fall' if the state changes to LOW.
+ */
+ void step() {
+ if (signal() >= high_threshold)
+ set_state(State::HIGH);
+ else if (signal() <= low_threshold)
+ set_state(State::LOW);
+ }
+
+private:
+ const std::function signal;
+ const float low_threshold = {0.1f};
+ const float high_threshold = {0.9f};
+};
+}
diff --git a/plugins/community/repos/DHE-Modules/src/util/d-latch.h b/plugins/community/repos/DHE-Modules/src/util/d-latch.h
new file mode 100644
index 00000000..58b479be
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/util/d-latch.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include
+#include
+
+#include "latch.h"
+
+namespace rack_plugin_DHE_Modules {
+
+/**
+ * A latch that can be set and reset.
+ */
+struct DLatch : Latch {
+ /**
+ * Sets the latch HIGH. Fires 'rise' if the latch was not already HIGH.
+ */
+ void set() { set_state(State::HIGH); }
+
+ /**
+ * Sets the latch LOW. Fires 'fall' if the latch was not already LOW.
+ */
+ void reset() { set_state(State::LOW); }
+};
+}
diff --git a/plugins/community/repos/DHE-Modules/src/util/latch.h b/plugins/community/repos/DHE-Modules/src/util/latch.h
new file mode 100644
index 00000000..4173b4b9
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/util/latch.h
@@ -0,0 +1,66 @@
+#pragma once
+
+#include
+#include
+
+namespace rack_plugin_DHE_Modules {
+
+class Latch {
+
+public:
+ bool is_high() const { return state==State::HIGH; }
+
+ /**
+ * Suspends firing events.
+ */
+ void disable() {
+ enabled = false;
+ }
+
+ /**
+ * Resumes firing events.
+ */
+ void enable() {
+ enabled = true;
+ }
+
+ /**
+ * Registers an action to be called on each rising edge.
+ * @param action called on each rising edge
+ */
+ void on_rise(std::function action) {
+ rise_actions.push_back(std::move(action));
+ }
+
+ /**
+ * Registers an action to be called on each falling edge.
+ * @param action called on each falling edge
+ */
+ void on_fall(std::function action) {
+ fall_actions.push_back(std::move(action));
+ }
+
+protected:
+ enum class State {
+ UNKNOWN, LOW, HIGH
+ } state = State::UNKNOWN;
+
+ void set_state(State incoming_state) {
+ if (state==incoming_state) return;
+ state = incoming_state;
+ fire(state==State::HIGH ? rise_actions : fall_actions);
+ }
+
+private:
+ bool enabled = true;
+ std::vector> rise_actions;
+ std::vector> fall_actions;
+
+ void fire(const std::vector> &actions) const {
+ if (!enabled)
+ return;
+ for (const auto &action : actions)
+ action();
+ }
+};
+}
diff --git a/plugins/community/repos/DHE-Modules/src/util/mode.h b/plugins/community/repos/DHE-Modules/src/util/mode.h
new file mode 100644
index 00000000..82d081d7
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/util/mode.h
@@ -0,0 +1,67 @@
+#pragma once
+
+#include
+#include
+#include "util/d-flip-flop.h"
+
+namespace rack_plugin_DHE_Modules {
+struct Mode {
+ void enter() { fire(entry_actions); }
+ void exit() { fire(exit_actions); }
+ void step() { fire(step_actions); }
+
+ void on_entry(std::function action) {
+ entry_actions.push_back(std::move(action));
+ }
+
+ void on_exit(std::function action) {
+ exit_actions.push_back(std::move(action));
+ }
+
+ void on_step(std::function action) {
+ step_actions.push_back(std::move(action));
+ }
+
+private:
+ void fire(const std::vector> &actions) {
+ for (const auto &action : actions) action();
+ }
+
+ std::vector> entry_actions;
+ std::vector> exit_actions;
+ std::vector> step_actions;
+};
+
+class SubmodeSwitch : public Mode {
+ DFlipFlop submode_switch;
+ Mode *submode;
+
+ void enter_submode(Mode *incoming_submode) {
+ submode->exit();
+ submode = incoming_submode;
+ submode->enter();
+ }
+
+public:
+ SubmodeSwitch(std::function switch_signal, Mode *low_mode, Mode *high_mode) :
+ submode_switch{std::move(switch_signal)}, submode{low_mode} {
+ submode_switch.on_rise([this, high_mode] {
+ enter_submode(high_mode);
+ });
+ submode_switch.on_fall([this, low_mode] {
+ enter_submode(low_mode);
+ });
+
+ on_entry([this] {
+ submode->enter();
+ });
+ on_step([this] {
+ submode_switch.step();
+ submode->step();
+ });
+ on_exit([this] {
+ submode->exit();
+ });
+ }
+};
+}
diff --git a/plugins/community/repos/DHE-Modules/src/util/ramp.h b/plugins/community/repos/DHE-Modules/src/util/ramp.h
new file mode 100644
index 00000000..d8bbfc3d
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/util/ramp.h
@@ -0,0 +1,116 @@
+#pragma once
+
+#include
+#include
+
+#include "d-latch.h"
+#include "sigmoid.h"
+
+namespace rack_plugin_DHE_Modules {
+
+/**
+ * A ramp that advances its phase from 0 to 1 in increments supplied by a given
+ * supplier.
+ *
+ * When the phase advances to 1, the ramp fires 'completion' and stops
+ * advancing.
+ */
+class Ramp {
+public:
+ /*!
+ * Constructs a ramp that advances its phase on each step by the amount
+ * supplied by the given supplier.
+ *
+ * The newly constructed ramp is at phase 0 and inactive.
+ *
+ * @param phase_increment_supplier supplies the amount to advance the phase on
+ * each step
+ */
+ explicit Ramp(std::function phase_increment_supplier)
+ : phase_increment{std::move(phase_increment_supplier)} {
+ stop();
+ }
+
+ /**
+ * Sets the phase to 0, actives the ramp, and fires 'start'. Subsequent steps
+ * will advance the phase.
+ */
+ void start() {
+ progress = 0.0;
+ active.enable();
+ active.set();
+ }
+
+ /*!
+ * Sets the phase to 0 and deactives the ramp. Subsequent steps will not
+ * advance the phase.
+ *
+ * Stopping the ramp does not fire any events.
+ */
+ void stop() {
+ progress = 0.0;
+ active.disable();
+ active.reset();
+ }
+
+ /**
+ * Advances the phase by the amount supplied by the phase increment supplier.
+ *
+ * If the phase advances to 1, the ramp fires 'completion' and becomes
+ * inactive.
+ *
+ * If the ramp is inactive, this function has no effect.
+ */
+ void step() {
+ if (!is_active())
+ return;
+
+ progress = UNIPOLAR_PHASE_RANGE.clamp(progress + phase_increment());
+
+ if (progress >= 1.0f) {
+ active.reset();
+ };
+ }
+
+ /**
+ * Indicates whether the ramp is active.
+ *
+ * @return whether the ramp is active
+ */
+ bool is_active() const { return active.is_high(); }
+
+ /**
+ * Returns the ramp's phase.
+ *
+ * If the ramp is active, the phase indicates the ramp's progress
+ * from 0 to 1.
+ *
+ * If the ramp is inactive, the phase is 1 (if the ramp became inactive by
+ * completing its advancement) or 0 (if the ramp was stopped).
+ *
+ * @return the ramp's phase
+ */
+ float phase() const { return progress; }
+
+ /**
+ * Registers an action to be called when the ramp starts.
+ * @param action called when the ramp starts
+ */
+ void on_start(std::function action) {
+ active.on_rise(std::move(action));
+ }
+
+ /**
+ * Registers an action to be called when the ramp's phase advances to 1.
+ * @param action called when the ramp's phase advances to 1
+ */
+ void on_completion(std::function action) {
+ active.on_fall(std::move(action));
+ }
+
+private:
+ float progress = 0.0f;
+ DLatch active{};
+ const std::function phase_increment;
+};
+}
diff --git a/plugins/community/repos/DHE-Modules/src/util/range.h b/plugins/community/repos/DHE-Modules/src/util/range.h
new file mode 100644
index 00000000..9f7b406a
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/util/range.h
@@ -0,0 +1,41 @@
+#pragma once
+
+namespace rack_plugin_DHE_Modules {
+
+inline float scale(float proportion, float lower_bound, float upper_bound) {
+ return proportion*(upper_bound - lower_bound) + lower_bound;
+}
+
+struct Range {
+ const float lower_bound;
+
+ const float upper_bound;
+
+ constexpr Range(float lower_bound, float upper_bound) noexcept : lower_bound(lower_bound), upper_bound(upper_bound) {}
+
+ float scale(float proportion) const {
+ return rack_plugin_DHE_Modules::scale(proportion, lower_bound, upper_bound);
+ }
+
+ float scale(bool state) const {
+ return state ? upper_bound : lower_bound;
+ }
+
+ float normalize(float member) const {
+ return (member - lower_bound)/(upper_bound - lower_bound);
+ }
+
+ float clamp(float f) const {
+ if (f < lower_bound)
+ return lower_bound;
+ if (f > upper_bound)
+ return upper_bound;
+ return f;
+ }
+};
+
+constexpr auto UNIPOLAR_PHASE_RANGE = Range{0.0f, 1.0f};
+constexpr auto BIPOLAR_PHASE_RANGE = Range{-1.0f, 1.0f};
+constexpr auto UNIPOLAR_SIGNAL_RANGE = Range{0.0f, 10.0f};
+constexpr auto BIPOLAR_SIGNAL_RANGE = Range{-5.0f, 5.0f};
+}
diff --git a/plugins/community/repos/DHE-Modules/src/util/sigmoid.h b/plugins/community/repos/DHE-Modules/src/util/sigmoid.h
new file mode 100644
index 00000000..4e4814d6
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/src/util/sigmoid.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include
+#include
+
+#include "range.h"
+
+namespace rack_plugin_DHE_Modules {
+
+inline float sigmoid(float x, float curvature) {
+ static constexpr auto precision = 1e-4f;
+ static constexpr auto max_curvature = 1.0f - precision;
+ static const auto curvature_range = Range{-max_curvature, max_curvature};
+
+ curvature = curvature_range.clamp(curvature);
+ x = BIPOLAR_PHASE_RANGE.clamp(x);
+
+ return (x - x*curvature)/(curvature - std::abs(x)*2.0f*curvature + 1.0f);
+}
+}
diff --git a/plugins/community/repos/DHE-Modules/test/booster-stage-tests.cpp b/plugins/community/repos/DHE-Modules/test/booster-stage-tests.cpp
new file mode 100644
index 00000000..1b3bb54d
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/test/booster-stage-tests.cpp
@@ -0,0 +1,6 @@
+#include
+#include "catch/catch.hpp"
+
+TEST_CASE("Booster Stage Module") {
+ DHE::BoosterStageModule module{};
+}
\ No newline at end of file
diff --git a/plugins/community/repos/DHE-Modules/test/catch/catch.hpp b/plugins/community/repos/DHE-Modules/test/catch/catch.hpp
new file mode 100644
index 00000000..160bad07
--- /dev/null
+++ b/plugins/community/repos/DHE-Modules/test/catch/catch.hpp
@@ -0,0 +1,12749 @@
+/*
+ * Catch v2.1.1
+ * Generated: 2018-01-26 16:04:07.190063
+ * ----------------------------------------------------------
+ * This file has been merged from multiple headers. Please don't edit it directly
+ * Copyright (c) 2018 Two Blue Cubes Ltd. All rights reserved.
+ *
+ * Distributed under the Boost Software License, Version 1.0. (See accompanying
+ * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+// start catch.hpp
+
+
+#ifdef __clang__
+# pragma clang system_header
+#elif defined __GNUC__
+# pragma GCC system_header
+#endif
+
+// start catch_suppress_warnings.h
+
+#ifdef __clang__
+# ifdef __ICC // icpc defines the __clang__ macro
+# pragma warning(push)
+# pragma warning(disable: 161 1682)
+# else // __ICC
+# pragma clang diagnostic ignored "-Wunused-variable"
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wpadded"
+# pragma clang diagnostic ignored "-Wswitch-enum"
+# pragma clang diagnostic ignored "-Wcovered-switch-default"
+# endif
+#elif defined __GNUC__
+# pragma GCC diagnostic ignored "-Wunused-variable"
+# pragma GCC diagnostic ignored "-Wparentheses"
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wpadded"
+#endif
+// end catch_suppress_warnings.h
+#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
+# define CATCH_IMPL
+# define CATCH_CONFIG_ALL_PARTS
+#endif
+
+// In the impl file, we want to have access to all parts of the headers
+// Can also be used to sanely support PCHs
+#if defined(CATCH_CONFIG_ALL_PARTS)
+# define CATCH_CONFIG_EXTERNAL_INTERFACES
+# if defined(CATCH_CONFIG_DISABLE_MATCHERS)
+# undef CATCH_CONFIG_DISABLE_MATCHERS
+# endif
+# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
+#endif
+
+#if !defined(CATCH_CONFIG_IMPL_ONLY)
+// start catch_platform.h
+
+#ifdef __APPLE__
+# include
+# if TARGET_OS_OSX == 1
+# define CATCH_PLATFORM_MAC
+# elif TARGET_OS_IPHONE == 1
+# define CATCH_PLATFORM_IPHONE
+# endif
+
+#elif defined(linux) || defined(__linux) || defined(__linux__)
+# define CATCH_PLATFORM_LINUX
+
+#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
+# define CATCH_PLATFORM_WINDOWS
+#endif
+
+// end catch_platform.h
+
+#ifdef CATCH_IMPL
+# ifndef CLARA_CONFIG_MAIN
+# define CLARA_CONFIG_MAIN_NOT_DEFINED
+# define CLARA_CONFIG_MAIN
+# endif
+#endif
+
+// start catch_user_interfaces.h
+
+namespace Catch {
+ unsigned int rngSeed();
+}
+
+// end catch_user_interfaces.h
+// start catch_tag_alias_autoregistrar.h
+
+// start catch_common.h
+
+// start catch_compiler_capabilities.h
+
+// Detect a number of compiler features - by compiler
+// The following features are defined:
+//
+// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
+// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?
+// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported?
+// ****************
+// Note to maintainers: if new toggles are added please document them
+// in configuration.md, too
+// ****************
+
+// In general each macro has a _NO_ form
+// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature.
+// Many features, at point of detection, define an _INTERNAL_ macro, so they
+// can be combined, en-mass, with the _NO_ forms later.
+
+#ifdef __cplusplus
+
+# if __cplusplus >= 201402L
+# define CATCH_CPP14_OR_GREATER
+# endif
+
+#endif
+
+#ifdef __clang__
+
+# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ _Pragma( "clang diagnostic push" ) \
+ _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
+ _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
+# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
+ _Pragma( "clang diagnostic pop" )
+
+# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+ _Pragma( "clang diagnostic push" ) \
+ _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
+# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+ _Pragma( "clang diagnostic pop" )
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// We know some environments not to support full POSIX signals
+#if defined(__CYGWIN__) || defined(__QNX__)
+
+# if !defined(CATCH_CONFIG_POSIX_SIGNALS)
+# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+# endif
+
+#endif
+
+#ifdef __OS400__
+# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+# define CATCH_CONFIG_COLOUR_NONE
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Cygwin
+#ifdef __CYGWIN__
+
+// Required for some versions of Cygwin to declare gettimeofday
+// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin
+# define _BSD_SOURCE
+
+#endif // __CYGWIN__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#ifdef _MSC_VER
+
+// Universal Windows platform does not support SEH
+// Or console colours (or console at all...)
+# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
+# define CATCH_CONFIG_COLOUR_NONE
+# else
+# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
+# endif
+
+#endif // _MSC_VER
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Use of __COUNTER__ is suppressed during code analysis in
+// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly
+// handled by it.
+// Otherwise all supported compilers support COUNTER macro,
+// but user still might want to turn it off
+#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L )
+ #define CATCH_INTERNAL_CONFIG_COUNTER
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
+# define CATCH_CONFIG_COUNTER
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH)
+# define CATCH_CONFIG_WINDOWS_SEH
+#endif
+// This is set by default, because we assume that unix compilers are posix-signal-compatible by default.
+#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)
+# define CATCH_CONFIG_POSIX_SIGNALS
+#endif
+
+#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
+# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
+# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
+#endif
+
+// end catch_compiler_capabilities.h
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
+#ifdef CATCH_CONFIG_COUNTER
+# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
+#else
+# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+#endif
+
+#include
+#include
+#include
+
+namespace Catch {
+
+ struct CaseSensitive { enum Choice {
+ Yes,
+ No
+ }; };
+
+ class NonCopyable {
+ NonCopyable( NonCopyable const& ) = delete;
+ NonCopyable( NonCopyable && ) = delete;
+ NonCopyable& operator = ( NonCopyable const& ) = delete;
+ NonCopyable& operator = ( NonCopyable && ) = delete;
+
+ protected:
+ NonCopyable();
+ virtual ~NonCopyable();
+ };
+
+ struct SourceLineInfo {
+
+ SourceLineInfo() = delete;
+ SourceLineInfo( char const* _file, std::size_t _line ) noexcept
+ : file( _file ),
+ line( _line )
+ {}
+
+ SourceLineInfo( SourceLineInfo const& other ) = default;
+ SourceLineInfo( SourceLineInfo && ) = default;
+ SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
+ SourceLineInfo& operator = ( SourceLineInfo && ) = default;
+
+ bool empty() const noexcept;
+ bool operator == ( SourceLineInfo const& other ) const noexcept;
+ bool operator < ( SourceLineInfo const& other ) const noexcept;
+
+ char const* file;
+ std::size_t line;
+ };
+
+ std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
+
+ // Use this in variadic streaming macros to allow
+ // >> +StreamEndStop
+ // as well as
+ // >> stuff +StreamEndStop
+ struct StreamEndStop {
+ std::string operator+() const;
+ };
+ template
+ T const& operator + ( T const& value, StreamEndStop ) {
+ return value;
+ }
+}
+
+#define CATCH_INTERNAL_LINEINFO \
+ ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) )
+
+// end catch_common.h
+namespace Catch {
+
+ struct RegistrarForTagAliases {
+ RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
+ };
+
+} // end namespace Catch
+
+#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \
+ CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
+
+// end catch_tag_alias_autoregistrar.h
+// start catch_test_registry.h
+
+// start catch_interfaces_testcase.h
+
+#include
+#include
+
+namespace Catch {
+
+ class TestSpec;
+
+ struct ITestInvoker {
+ virtual void invoke () const = 0;
+ virtual ~ITestInvoker();
+ };
+
+ using ITestCasePtr = std::shared_ptr;
+
+ class TestCase;
+ struct IConfig;
+
+ struct ITestCaseRegistry {
+ virtual ~ITestCaseRegistry();
+ virtual std::vector const& getAllTests() const = 0;
+ virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0;
+ };
+
+ bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
+ std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config );
+ std::vector const& getAllTestCasesSorted( IConfig const& config );
+
+}
+
+// end catch_interfaces_testcase.h
+// start catch_stringref.h
+
+#include
+#include
+#include
+
+namespace Catch {
+
+ class StringData;
+
+ /// A non-owning string class (similar to the forthcoming std::string_view)
+ /// Note that, because a StringRef may be a substring of another string,
+ /// it may not be null terminated. c_str() must return a null terminated
+ /// string, however, and so the StringRef will internally take ownership
+ /// (taking a copy), if necessary. In theory this ownership is not externally
+ /// visible - but it does mean (substring) StringRefs should not be shared between
+ /// threads.
+ class StringRef {
+ public:
+ using size_type = std::size_t;
+
+ private:
+ friend struct StringRefTestAccess;
+
+ char const* m_start;
+ size_type m_size;
+
+ char* m_data = nullptr;
+
+ void takeOwnership();
+
+ static constexpr char const* const s_empty = "";
+
+ public: // construction/ assignment
+ StringRef() noexcept
+ : StringRef( s_empty, 0 )
+ {}
+
+ StringRef( StringRef const& other ) noexcept
+ : m_start( other.m_start ),
+ m_size( other.m_size )
+ {}
+
+ StringRef( StringRef&& other ) noexcept
+ : m_start( other.m_start ),
+ m_size( other.m_size ),
+ m_data( other.m_data )
+ {
+ other.m_data = nullptr;
+ }
+
+ StringRef( char const* rawChars ) noexcept;
+
+ StringRef( char const* rawChars, size_type size ) noexcept
+ : m_start( rawChars ),
+ m_size( size )
+ {}
+
+ StringRef( std::string const& stdString ) noexcept
+ : m_start( stdString.c_str() ),
+ m_size( stdString.size() )
+ {}
+
+ ~StringRef() noexcept {
+ delete[] m_data;
+ }
+
+ auto operator = ( StringRef const &other ) noexcept -> StringRef& {
+ delete[] m_data;
+ m_data = nullptr;
+ m_start = other.m_start;
+ m_size = other.m_size;
+ return *this;
+ }
+
+ operator std::string() const;
+
+ void swap( StringRef& other ) noexcept;
+
+ public: // operators
+ auto operator == ( StringRef const& other ) const noexcept -> bool;
+ auto operator != ( StringRef const& other ) const noexcept -> bool;
+
+ auto operator[] ( size_type index ) const noexcept -> char;
+
+ public: // named queries
+ auto empty() const noexcept -> bool {
+ return m_size == 0;
+ }
+ auto size() const noexcept -> size_type {
+ return m_size;
+ }
+
+ auto numberOfCharacters() const noexcept -> size_type;
+ auto c_str() const -> char const*;
+
+ public: // substrings and searches
+ auto substr( size_type start, size_type size ) const noexcept -> StringRef;
+
+ private: // ownership queries - may not be consistent between calls
+ auto isOwned() const noexcept -> bool;
+ auto isSubstring() const noexcept -> bool;
+ auto data() const noexcept -> char const*;
+ };
+
+ auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string;
+ auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string;
+ auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string;
+
+ auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&;
+
+ inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
+ return StringRef( rawChars, size );
+ }
+
+} // namespace Catch
+
+// end catch_stringref.h
+namespace Catch {
+
+template
+class TestInvokerAsMethod : public ITestInvoker {
+ void (C::*m_testAsMethod)();
+public:
+ TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {}
+
+ void invoke() const override {
+ C obj;
+ (obj.*m_testAsMethod)();
+ }
+};
+
+auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*;
+
+template
+auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* {
+ return new(std::nothrow) TestInvokerAsMethod( testAsMethod );
+}
+
+struct NameAndTags {
+ NameAndTags( StringRef name_ = StringRef(), StringRef tags_ = StringRef() ) noexcept;
+ StringRef name;
+ StringRef tags;
+};
+
+struct AutoReg : NonCopyable {
+ AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept;
+ ~AutoReg();
+};
+
+} // end namespace Catch
+
+#if defined(CATCH_CONFIG_DISABLE)
+ #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \
+ static void TestName()
+ #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \
+ namespace{ \
+ struct TestName : ClassName { \
+ void test(); \
+ }; \
+ } \
+ void TestName::test()
+
+#endif
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
+ static void TestName(); \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, "", Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
+ CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
+ static void TestName()
+ #define INTERNAL_CATCH_TESTCASE( ... ) \
+ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
+ CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ \
+ struct TestName : ClassName{ \
+ void test(); \
+ }; \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
+ } \
+ CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
+ void TestName::test()
+ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
+ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, "", Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
+ CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
+
+// end catch_test_registry.h
+// start catch_capture.hpp
+
+// start catch_assertionhandler.h
+
+// start catch_assertioninfo.h
+
+// start catch_result_type.h
+
+namespace Catch {
+
+ // ResultWas::OfType enum
+ struct ResultWas { enum OfType {
+ Unknown = -1,
+ Ok = 0,
+ Info = 1,
+ Warning = 2,
+
+ FailureBit = 0x10,
+
+ ExpressionFailed = FailureBit | 1,
+ ExplicitFailure = FailureBit | 2,
+
+ Exception = 0x100 | FailureBit,
+
+ ThrewException = Exception | 1,
+ DidntThrowException = Exception | 2,
+
+ FatalErrorCondition = 0x200 | FailureBit
+
+ }; };
+
+ bool isOk( ResultWas::OfType resultType );
+ bool isJustInfo( int flags );
+
+ // ResultDisposition::Flags enum
+ struct ResultDisposition { enum Flags {
+ Normal = 0x01,
+
+ ContinueOnFailure = 0x02, // Failures fail test, but execution continues
+ FalseTest = 0x04, // Prefix expression with !
+ SuppressFail = 0x08 // Failures are reported but do not fail the test
+ }; };
+
+ ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs );
+
+ bool shouldContinueOnFailure( int flags );
+ inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
+ bool shouldSuppressFailure( int flags );
+
+} // end namespace Catch
+
+// end catch_result_type.h
+namespace Catch {
+
+ struct AssertionInfo
+ {
+ StringRef macroName;
+ SourceLineInfo lineInfo;
+ StringRef capturedExpression;
+ ResultDisposition::Flags resultDisposition;
+
+ // We want to delete this constructor but a compiler bug in 4.8 means
+ // the struct is then treated as non-aggregate
+ //AssertionInfo() = delete;
+ };
+
+} // end namespace Catch
+
+// end catch_assertioninfo.h
+// start catch_decomposer.h
+
+// start catch_tostring.h
+
+#include
+#include
+#include
+#include
+// start catch_stream.h
+
+#include
+#include
+#include
+
+namespace Catch {
+
+ std::ostream& cout();
+ std::ostream& cerr();
+ std::ostream& clog();
+
+ class StringRef;
+
+ struct IStream {
+ virtual ~IStream();
+ virtual std::ostream& stream() const = 0;
+ };
+
+ auto makeStream( StringRef const &filename ) -> IStream const*;
+
+ class ReusableStringStream {
+ std::size_t m_index;
+ std::ostream* m_oss;
+ public:
+ ReusableStringStream();
+ ~ReusableStringStream();
+
+ auto str() const -> std::string;
+
+ template
+ auto operator << ( T const& value ) -> ReusableStringStream& {
+ *m_oss << value;
+ return *this;
+ }
+ auto get() -> std::ostream& { return *m_oss; }
+
+ static void cleanup();
+ };
+}
+
+// end catch_stream.h
+
+#ifdef __OBJC__
+// start catch_objc_arc.hpp
+
+#import
+
+#ifdef __has_feature
+#define CATCH_ARC_ENABLED __has_feature(objc_arc)
+#else
+#define CATCH_ARC_ENABLED 0
+#endif
+
+void arcSafeRelease( NSObject* obj );
+id performOptionalSelector( id obj, SEL sel );
+
+#if !CATCH_ARC_ENABLED
+inline void arcSafeRelease( NSObject* obj ) {
+ [obj release];
+}
+inline id performOptionalSelector( id obj, SEL sel ) {
+ if( [obj respondsToSelector: sel] )
+ return [obj performSelector: sel];
+ return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED
+#define CATCH_ARC_STRONG
+#else
+inline void arcSafeRelease( NSObject* ){}
+inline id performOptionalSelector( id obj, SEL sel ) {
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+#endif
+ if( [obj respondsToSelector: sel] )
+ return [obj performSelector: sel];
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
+#define CATCH_ARC_STRONG __strong
+#endif
+
+// end catch_objc_arc.hpp
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless
+#endif
+
+// We need a dummy global operator<< so we can bring it into Catch namespace later
+struct Catch_global_namespace_dummy {};
+std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy);
+
+namespace Catch {
+ // Bring in operator<< from global namespace into Catch namespace
+ using ::operator<<;
+
+ namespace Detail {
+
+ extern const std::string unprintableString;
+
+ std::string rawMemoryToString( const void *object, std::size_t size );
+
+ template
+ std::string rawMemoryToString( const T& object ) {
+ return rawMemoryToString( &object, sizeof(object) );
+ }
+
+ template
+ class IsStreamInsertable {
+ template
+ static auto test(int)
+ -> decltype(std::declval() << std::declval(), std::true_type());
+
+ template
+ static auto test(...)->std::false_type;
+
+ public:
+ static const bool value = decltype(test(0))::value;
+ };
+
+ template
+ std::string convertUnknownEnumToString( E e );
+
+ template
+ typename std::enable_if::value, std::string>::type convertUnstreamable( T const& ) {
+ return Detail::unprintableString;
+ };
+ template
+ typename std::enable_if::value, std::string>::type convertUnstreamable( T const& value ) {
+ return convertUnknownEnumToString( value );
+ };
+
+ } // namespace Detail
+
+ // If we decide for C++14, change these to enable_if_ts
+ template
+ struct StringMaker {
+ template
+ static
+ typename std::enable_if<::Catch::Detail::IsStreamInsertable::value, std::string>::type
+ convert(const Fake& value) {
+ ReusableStringStream rss;
+ rss << value;
+ return rss.str();
+ }
+
+ template
+ static
+ typename std::enable_if::value, std::string>::type
+ convert( const Fake& value ) {
+ return Detail::convertUnstreamable( value );
+ }
+ };
+
+ namespace Detail {
+
+ // This function dispatches all stringification requests inside of Catch.
+ // Should be preferably called fully qualified, like ::Catch::Detail::stringify
+ template
+ std::string stringify(const T& e) {
+ return ::Catch::StringMaker::type>::type>::convert(e);
+ }
+
+ template
+ std::string convertUnknownEnumToString( E e ) {
+ return ::Catch::Detail::stringify(static_cast::type>(e));
+ }
+
+ } // namespace Detail
+
+ // Some predefined specializations
+
+ template<>
+ struct StringMaker {
+ static std::string convert(const std::string& str);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(const std::wstring& wstr);
+ };
+
+ template<>
+ struct StringMaker {
+ static std::string convert(char const * str);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(char * str);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(wchar_t const * str);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(wchar_t * str);
+ };
+
+ template
+ struct StringMaker {
+ static std::string convert(const char* str) {
+ return ::Catch::Detail::stringify(std::string{ str });
+ }
+ };
+ template
+ struct StringMaker {
+ static std::string convert(const char* str) {
+ return ::Catch::Detail::stringify(std::string{ str });
+ }
+ };
+ template
+ struct StringMaker {
+ static std::string convert(const char* str) {
+ return ::Catch::Detail::stringify(std::string{ str });
+ }
+ };
+
+ template<>
+ struct StringMaker {
+ static std::string convert(int value);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(long value);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(long long value);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(unsigned int value);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(unsigned long value);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(unsigned long long value);
+ };
+
+ template<>
+ struct StringMaker {
+ static std::string convert(bool b);
+ };
+
+ template<>
+ struct StringMaker {
+ static std::string convert(char c);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(signed char c);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(unsigned char c);
+ };
+
+ template<>
+ struct StringMaker {
+ static std::string convert(std::nullptr_t);
+ };
+
+ template<>
+ struct StringMaker {
+ static std::string convert(float value);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(double value);
+ };
+
+ template
+ struct StringMaker {
+ template
+ static std::string convert(U* p) {
+ if (p) {
+ return ::Catch::Detail::rawMemoryToString(p);
+ } else {
+ return "nullptr";
+ }
+ }
+ };
+
+ template
+ struct StringMaker {
+ static std::string convert(R C::* p) {
+ if (p) {
+ return ::Catch::Detail::rawMemoryToString(p);
+ } else {
+ return "nullptr";
+ }
+ }
+ };
+
+ namespace Detail {
+ template
+ std::string rangeToString(InputIterator first, InputIterator last) {
+ ReusableStringStream rss;
+ rss << "{ ";
+ if (first != last) {
+ rss << ::Catch::Detail::stringify(*first);
+ for (++first; first != last; ++first)
+ rss << ", " << ::Catch::Detail::stringify(*first);
+ }
+ rss << " }";
+ return rss.str();
+ }
+ }
+
+#ifdef __OBJC__
+ template<>
+ struct StringMaker {
+ static std::string convert(NSString * nsstring) {
+ if (!nsstring)
+ return "nil";
+ return std::string("@") + [nsstring UTF8String];
+ }
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(NSObject* nsObject) {
+ return ::Catch::Detail::stringify([nsObject description]);
+ }
+
+ };
+ namespace Detail {
+ inline std::string stringify( NSString* nsstring ) {
+ return StringMaker::convert( nsstring );
+ }
+
+ } // namespace Detail
+#endif // __OBJC__
+
+} // namespace Catch
+
+//////////////////////////////////////////////////////
+// Separate std-lib types stringification, so it can be selectively enabled
+// This means that we do not bring in
+
+#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS)
+# define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
+# define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
+# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
+#endif
+
+// Separate std::pair specialization
+#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER)
+#include
+namespace Catch {
+ template
+ struct StringMaker > {
+ static std::string convert(const std::pair& pair) {
+ ReusableStringStream rss;
+ rss << "{ "
+ << ::Catch::Detail::stringify(pair.first)
+ << ", "
+ << ::Catch::Detail::stringify(pair.second)
+ << " }";
+ return rss.str();
+ }
+ };
+}
+#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
+
+// Separate std::tuple specialization
+#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
+#include
+namespace Catch {
+ namespace Detail {
+ template<
+ typename Tuple,
+ std::size_t N = 0,
+ bool = (N < std::tuple_size::value)
+ >
+ struct TupleElementPrinter {
+ static void print(const Tuple& tuple, std::ostream& os) {
+ os << (N ? ", " : " ")
+ << ::Catch::Detail::stringify(std::get(tuple));
+ TupleElementPrinter::print(tuple, os);
+ }
+ };
+
+ template<
+ typename Tuple,
+ std::size_t N
+ >
+ struct TupleElementPrinter {
+ static void print(const Tuple&, std::ostream&) {}
+ };
+
+ }
+
+ template
+ struct StringMaker> {
+ static std::string convert(const std::tuple& tuple) {
+ ReusableStringStream rss;
+ rss << '{';
+ Detail::TupleElementPrinter>::print(tuple, rss.get());
+ rss << " }";
+ return rss.str();
+ }
+ };
+}
+#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
+
+namespace Catch {
+ struct not_this_one {}; // Tag type for detecting which begin/ end are being selected
+
+ // Import begin/ end from std here so they are considered alongside the fallback (...) overloads in this namespace
+ using std::begin;
+ using std::end;
+
+ not_this_one begin( ... );
+ not_this_one end( ... );
+
+ template
+ struct is_range {
+ static const bool value =
+ !std::is_same())), not_this_one>::value &&
+ !std::is_same())), not_this_one>::value;
+ };
+
+ template
+ std::string rangeToString( Range const& range ) {
+ return ::Catch::Detail::rangeToString( begin( range ), end( range ) );
+ }
+
+ // Handle vector specially
+ template
+ std::string rangeToString( std::vector