diff --git a/.github/workflows/build-plugin.yml b/.github/workflows/build-plugin.yml
new file mode 100644
index 0000000..961853e
--- /dev/null
+++ b/.github/workflows/build-plugin.yml
@@ -0,0 +1,84 @@
+name: Build VCV Rack Plugin
+on: [push, pull_request]
+
+env:
+ rack-sdk-version: 2.5.1
+ rack-plugin-toolchain-dir: /home/build/rack-plugin-toolchain
+
+defaults:
+ run:
+ shell: bash
+
+jobs:
+ build:
+ name: ${{ matrix.platform }}
+ runs-on: ubuntu-latest
+ container:
+ image: ghcr.io/qno/rack-plugin-toolchain-win-linux
+ options: --user root
+ strategy:
+ fail-fast: false
+ matrix:
+ platform: [win-x64, lin-x64]
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ submodules: recursive
+ - name: Build plugin
+ run: |
+ export PLUGIN_DIR=$GITHUB_WORKSPACE
+ pushd ${{ env.rack-plugin-toolchain-dir }}
+ make plugin-build-${{ matrix.platform }}
+ - name: Upload artifact
+ uses: actions/upload-artifact@v3
+ with:
+ path: ${{ env.rack-plugin-toolchain-dir }}/plugin-build
+ name: ${{ matrix.platform }}
+
+ build-mac:
+ name: mac
+ runs-on: macos-12
+ strategy:
+ fail-fast: false
+ matrix:
+ platform: [x64, arm64]
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ submodules: recursive
+ - name: Get Rack-SDK
+ run: |
+ pushd $HOME
+ curl -o Rack-SDK.zip https://vcvrack.com/downloads/Rack-SDK-${{ env.rack-sdk-version }}-mac-${{ matrix.platform }}.zip
+ unzip Rack-SDK.zip
+ - name: Build plugin
+ run: |
+ CROSS_COMPILE_TARGET_x64=x86_64-apple-darwin
+ CROSS_COMPILE_TARGET_arm64=arm64-apple-darwin
+ export RACK_DIR=$HOME/Rack-SDK
+ export CROSS_COMPILE=$CROSS_COMPILE_TARGET_${{ matrix.platform }}
+ make dep
+ make dist
+ echo "Plugin architecture '$(lipo -archs plugin.dylib)'"
+ - name: Upload artifact
+ uses: actions/upload-artifact@v3
+ with:
+ path: dist/*.vcvplugin
+ name: mac-${{ matrix.platform }}
+
+ publish:
+ name: Publish plugin
+ runs-on: ubuntu-latest
+ needs: [build, build-mac]
+ steps:
+ - uses: actions/download-artifact@v3
+ with:
+ path: _artifacts
+ - uses: "marvinpinto/action-automatic-releases@latest"
+ with:
+ repo_token: "${{ secrets.GITHUB_TOKEN }}"
+ automatic_release_tag: "latest"
+ prerelease: true
+ title: "Development Build"
+ files: |
+ _artifacts/**/*.vcvplugin
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0b5427d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+/build
+/dist
+/*.so
+/*.dylib
+/*.dll
+.DS_Store
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..07b5733
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,23 @@
+# If RACK_DIR is not defined when calling the Makefile, default to two directories above
+RACK_DIR ?= ../..
+
+# FLAGS will be passed to both the C and C++ compiler
+FLAGS +=
+CFLAGS +=
+CXXFLAGS +=
+
+# Careful about linking to shared libraries, since you can't assume much about the user's environment and library search path.
+# Static libraries are fine, but they should be added to this plugin's build system.
+LDFLAGS +=
+
+# Add .cpp files to the build
+SOURCES += $(wildcard src/*.cpp)
+
+# Add files to the ZIP package when running `make dist`
+# The compiled plugin and "plugin.json" are automatically added.
+DISTRIBUTABLES += res
+DISTRIBUTABLES += $(wildcard LICENSE*)
+DISTRIBUTABLES += $(wildcard presets)
+
+# Include the Rack plugin Makefile framework
+include $(RACK_DIR)/plugin.mk
diff --git a/plugin.json b/plugin.json
new file mode 100644
index 0000000..be1f854
--- /dev/null
+++ b/plugin.json
@@ -0,0 +1,23 @@
+{
+ "slug": "BlackNoiseModular",
+ "name": "BlackNoiseModular",
+ "version": "2.0.0",
+ "license": "proprietary",
+ "brand": "BlackNoiseModular",
+ "author": "Alexandre Simon, Ewan Hemingway",
+ "authorEmail": "",
+ "authorUrl": "",
+ "pluginUrl": "",
+ "manualUrl": "",
+ "sourceUrl": "",
+ "donateUrl": "",
+ "changelogUrl": "",
+ "modules": [
+ {
+ "slug": "Cosmos",
+ "name": "Cosmos",
+ "description": "",
+ "tags": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/res/panels/Cosmos.svg b/res/panels/Cosmos.svg
new file mode 100644
index 0000000..8bef7a2
--- /dev/null
+++ b/res/panels/Cosmos.svg
@@ -0,0 +1,1735 @@
+
+
+
+
diff --git a/src/Cosmos.cpp b/src/Cosmos.cpp
new file mode 100644
index 0000000..21e1f44
--- /dev/null
+++ b/src/Cosmos.cpp
@@ -0,0 +1,177 @@
+#include "plugin.hpp"
+
+using namespace simd;
+
+struct Cosmos : Module {
+ enum ParamId {
+ PAD_X_PARAM,
+ PAD_Y_PARAM,
+ PARAMS_LEN
+ };
+ enum InputId {
+ X_INPUT,
+ Y_INPUT,
+ INPUTS_LEN
+ };
+ enum OutputId {
+ XOR_GATE_OUTPUT,
+ XOR_TRIG_OUTPUT,
+ TZ_CLIPPER_OUTPUT,
+ OR_GATE_OUTPUT,
+ AND_GATE_OUTPUT,
+ MAX_OUTPUT,
+ MIN_OUTPUT,
+ SUM_OUTPUT,
+ ORG_TRIG_OUTPUT,
+ X_OUTPUT,
+ Y_OUTPUT,
+ AND_TRIG_OUTPUT,
+ NOR_TRIG_OUTPUT,
+ INV_X_OUTPUT,
+ INV_Y_OUTPUT,
+ NAND_TRIG_OUTPUT,
+ DIFF_OUTPUT,
+ INV_MAX_OUTPUT,
+ INV_MIN_OUTPUT,
+ NOR_GATE_OUTPUT,
+ NAND_GATE_OUTPUT,
+ INV_TZ_CLIPPER_OUTPUT,
+ XNOR_GATE_OUTPUT,
+ XNOR_TRIG_OUTPUT,
+ OUTPUTS_LEN
+ };
+ enum LightId {
+ XOR_LIGHT,
+ MAX_LIGHT,
+ MIN_LIGHT,
+ SUM_LIGHT,
+ X_LIGHT,
+ Y_LIGHT,
+ INV_X_LIGHT,
+ INV_Y_LIGHT,
+ DIFF_LIGHT,
+ INV_MAX_LIGHT,
+ INV_MIN_LIGHT,
+ XNOR_LIGHT,
+ LIGHTS_LEN
+ };
+
+ Cosmos() {
+ config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN);
+ configParam(PAD_X_PARAM, 0.f, 1.f, 0.f, "");
+ configParam(PAD_Y_PARAM, 0.f, 1.f, 0.f, "");
+ configInput(X_INPUT, "X");
+ configInput(Y_INPUT, "Y");
+ configOutput(XOR_GATE_OUTPUT, "XOR gate");
+ configOutput(XOR_TRIG_OUTPUT, "XOR trigger");
+ configOutput(TZ_CLIPPER_OUTPUT, "Through-zero clipper");
+ configOutput(OR_GATE_OUTPUT, "OR gate");
+ configOutput(AND_GATE_OUTPUT, "AND gate");
+ configOutput(MAX_OUTPUT, "Maximum");
+ configOutput(MIN_OUTPUT, "Minimum");
+ configOutput(SUM_OUTPUT, "Sum");
+ configOutput(ORG_TRIG_OUTPUT, "OR-gate trigger");
+ configOutput(X_OUTPUT, "X");
+ configOutput(Y_OUTPUT, "Y");
+ configOutput(AND_TRIG_OUTPUT, "AND trigger");
+ configOutput(NOR_TRIG_OUTPUT, "NOR trigger");
+ configOutput(INV_X_OUTPUT, "X (inverted)");
+ configOutput(INV_Y_OUTPUT, "Y (inverted)");
+ configOutput(NAND_TRIG_OUTPUT, "NAND trigger");
+ configOutput(DIFF_OUTPUT, "Difference");
+ configOutput(INV_MAX_OUTPUT, "Maximum (inverted)");
+ configOutput(INV_MIN_OUTPUT, "Minimum (inverted)");
+ configOutput(NOR_GATE_OUTPUT, "NOR gate");
+ configOutput(NAND_GATE_OUTPUT, "NAND gate");
+ configOutput(INV_TZ_CLIPPER_OUTPUT, "Ternary clipper (inverted)");
+ configOutput(XNOR_GATE_OUTPUT, "XNOR gate");
+ configOutput(XNOR_TRIG_OUTPUT, "XNOR trigger");
+ }
+
+ void process(const ProcessArgs& args) override {
+
+ const int numActivePolyphonyChannels = std::max({1, inputs[X_INPUT].getChannels(), inputs[Y_INPUT].getChannels()});
+
+ for (int c = 0; c < numActivePolyphonyChannels; c += 4) {
+
+ const float_4 x = inputs[X_INPUT].getPolyVoltage(c);
+ const float_4 y = inputs[Y_INPUT].getPolyVoltage(c);
+
+
+ outputs[X_OUTPUT].setVoltageSimd(x, c);
+ outputs[Y_OUTPUT].setVoltageSimd(y, c);
+
+ outputs[SUM_OUTPUT].setVoltageSimd(x + y, c);
+ outputs[DIFF_OUTPUT].setVoltageSimd(x - y, c);
+
+ outputs[MAX_OUTPUT].setVoltageSimd(ifelse(x > y, x, y), c);
+ outputs[MIN_OUTPUT].setVoltageSimd(ifelse(x > y, y, x), c);
+
+ outputs[INV_X_OUTPUT].setVoltageSimd(-x, c);
+ outputs[INV_Y_OUTPUT].setVoltageSimd(-y, c);
+ }
+
+ outputs[X_OUTPUT].setChannels(numActivePolyphonyChannels);
+ outputs[Y_OUTPUT].setChannels(numActivePolyphonyChannels);
+ }
+};
+
+
+struct CosmosWidget : ModuleWidget {
+ CosmosWidget(Cosmos* module) {
+ setModule(module);
+ setPanel(createPanel(asset::plugin(pluginInstance, "res/panels/Cosmos.svg")));
+
+ addChild(createWidget(Vec(RACK_GRID_WIDTH, 0)));
+ addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
+ addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
+ addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
+
+ addParam(createParamCentered(mm2px(Vec(6.47, 64.318)), module, Cosmos::PAD_X_PARAM));
+ addParam(createParamCentered(mm2px(Vec(64.275, 64.318)), module, Cosmos::PAD_Y_PARAM));
+
+ addInput(createInputCentered(mm2px(Vec(17.67, 64.347)), module, Cosmos::X_INPUT));
+ addInput(createInputCentered(mm2px(Vec(52.962, 64.347)), module, Cosmos::Y_INPUT));
+
+ addOutput(createOutputCentered(mm2px(Vec(17.677, 14.23)), module, Cosmos::XOR_GATE_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(52.981, 14.22)), module, Cosmos::XOR_TRIG_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(35.329, 21.201)), module, Cosmos::TZ_CLIPPER_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(10.428, 26.725)), module, Cosmos::OR_GATE_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(60.23, 26.725)), module, Cosmos::AND_GATE_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(17.67, 39.245)), module, Cosmos::MAX_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(52.986, 39.245)), module, Cosmos::MIN_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(35.329, 46.26)), module, Cosmos::SUM_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(10.44, 51.775)), module, Cosmos::ORG_TRIG_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(24.889, 51.775)), module, Cosmos::X_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(45.757, 51.775)), module, Cosmos::Y_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(60.206, 51.775)), module, Cosmos::AND_TRIG_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(10.453, 76.816)), module, Cosmos::NOR_TRIG_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(24.902, 76.816)), module, Cosmos::INV_X_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(45.769, 76.816)), module, Cosmos::INV_Y_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(60.218, 76.816)), module, Cosmos::NAND_TRIG_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(35.329, 82.331)), module, Cosmos::DIFF_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(17.672, 89.346)), module, Cosmos::INV_MAX_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(52.989, 89.346)), module, Cosmos::INV_MIN_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(10.428, 101.866)), module, Cosmos::NOR_GATE_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(60.23, 101.865)), module, Cosmos::NAND_GATE_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(35.329, 107.39)), module, Cosmos::INV_TZ_CLIPPER_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(17.677, 114.371)), module, Cosmos::XNOR_GATE_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(52.981, 114.361)), module, Cosmos::XNOR_TRIG_OUTPUT));
+
+ addChild(createLightCentered>(mm2px(Vec(35.331, 29.793)), module, Cosmos::XOR_LIGHT));
+ addChild(createLightCentered>(mm2px(Vec(26.279, 39.23)), module, Cosmos::MAX_LIGHT));
+ addChild(createLightCentered>(mm2px(Vec(44.376, 39.23)), module, Cosmos::MIN_LIGHT));
+ addChild(createLightCentered>(mm2px(Vec(35.3, 54.892)), module, Cosmos::SUM_LIGHT));
+ addChild(createLightCentered>(mm2px(Vec(29.372, 59.528)), module, Cosmos::X_LIGHT));
+ addChild(createLightCentered>(mm2px(Vec(41.292, 59.528)), module, Cosmos::Y_LIGHT));
+ addChild(createLightCentered>(mm2px(Vec(29.372, 69.052)), module, Cosmos::INV_X_LIGHT));
+ addChild(createLightCentered>(mm2px(Vec(41.292, 69.052)), module, Cosmos::INV_Y_LIGHT));
+ addChild(createLightCentered>(mm2px(Vec(35.3, 73.688)), module, Cosmos::DIFF_LIGHT));
+ addChild(createLightCentered>(mm2px(Vec(26.279, 89.35)), module, Cosmos::INV_MAX_LIGHT));
+ addChild(createLightCentered>(mm2px(Vec(44.376, 89.35)), module, Cosmos::INV_MIN_LIGHT));
+ addChild(createLightCentered>(mm2px(Vec(35.331, 98.787)), module, Cosmos::XNOR_LIGHT));
+ }
+};
+
+
+Model* modelCosmos = createModel("Cosmos");
\ No newline at end of file
diff --git a/src/plugin.cpp b/src/plugin.cpp
new file mode 100644
index 0000000..fe1225e
--- /dev/null
+++ b/src/plugin.cpp
@@ -0,0 +1,13 @@
+#include "plugin.hpp"
+
+
+Plugin* pluginInstance;
+
+
+void init(Plugin* p) {
+ pluginInstance = p;
+
+ // Add modules here
+ p->addModel(modelCosmos);
+
+}
diff --git a/src/plugin.hpp b/src/plugin.hpp
new file mode 100644
index 0000000..01f0265
--- /dev/null
+++ b/src/plugin.hpp
@@ -0,0 +1,11 @@
+#pragma once
+#include
+
+
+using namespace rack;
+
+// Declare the Plugin, defined in plugin.cpp
+extern Plugin* pluginInstance;
+
+// Declare each Model, defined in each module source file
+extern Model* modelCosmos;