I’m not a real programmer, but was able to describe a module idea to ChatGPT. I know chatbots hallucinate, but on the other hand, ChatGPT and other AIs are built now to make code. The robot created:
Makefile plugin.cpp plugin.hpp plugin.json SergeCurveBender.cpp and a matching SVG in a folder /res
It showed me how to use Terminal with the Rack SDK downloads to compile the plugin.dylib. I used the Silicon version of SDK to match the version of Rack I have.
The chatbot and I have checked and double-checked what’s written in the various files. I used BBEdit, not TextEdit, to save the robot’s code into files with the proper extensions, because I heard TextEdit sometimes screws things up.
The only ambiguity I encountered was the robot first created a plugin.json, then mentioned it should be manifest.json for Rack 2. But when I tried compiling that in Terminal, I got the message “can’t find plugin.json,” if I’m remembering correctly, so I changed the filename back to plugin.json.
Thank you for your wisdom! I got so excited once the robot told me it could help me build my VCV Rack dreams, but I’ve been crushed with disappointment ever since.
Makefile:
PLUGIN_NAME := SergeCurveBender
RACK_DIR ?= /Users/anneevans/Documents/Rack-SDK
PLUGIN_SLUG := SergeCurveBender
PLUGIN_VERSION := 1.0.0
include $(RACK_DIR)/plugin.mk```
plugin.hpp (which for some reason is ignoring the code-box)
#pragma once
#include "rack.hpp"
using namespace rack;
extern Plugin* pluginInstance;
extern Model* modelSergeCurveBender;```
plugin.cpp:
#include "plugin.hpp"
Plugin* pluginInstance;
void init(Plugin* p) {
pluginInstance = p;
p->addModel(modelSergeCurveBender);
}```
plugin.json:
{
"slug": "SergeCurveBender",
"name": "Serge Curve Bender",
"version": "1.0.0",
"author": "Anne Evans",
"license": "Proprietary"
}
The brains of the module:
// SergeCurveBender.cpp
#include "plugin.hpp"
struct SergeCurveBender : Module {
enum ParamIds {
BEND_PARAM,
CURVE_PARAM,
DRIVE_PARAM,
NUM_PARAMS
};
enum InputIds {
IN_INPUT,
BEND_INPUT,
CURVE_INPUT,
DRIVE_INPUT,
NUM_INPUTS
};
enum OutputIds {
OUT_OUTPUT,
NUM_OUTPUTS
};
enum LightIds {
NUM_LIGHTS
};
SergeCurveBender() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
configParam(BEND_PARAM, 0.f, 1.f, 0.f, "Bend Amount");
configParam(CURVE_PARAM, 0.f, 1.f, 0.f, "Curvature Amount");
configParam(DRIVE_PARAM, 0.f, 2.f, 1.f, "Drive Amount");
}
void process(const ProcessArgs& args) override {
float in = inputs[IN_INPUT].getVoltage();
float bend = params[BEND_PARAM].getValue();
if (inputs[BEND_INPUT].isConnected())
bend += inputs[BEND_INPUT].getVoltage() / 10.f;
bend = clamp(bend, 0.f, 1.f);
float curve = params[CURVE_PARAM].getValue();
if (inputs[CURVE_INPUT].isConnected())
curve += inputs[CURVE_INPUT].getVoltage() / 10.f;
curve = clamp(curve, 0.f, 1.f);
float drive = params[DRIVE_PARAM].getValue();
if (inputs[DRIVE_INPUT].isConnected())
drive += inputs[DRIVE_INPUT].getVoltage() / 10.f;
drive = clamp(drive, 0.f, 2.f);
in *= drive;
float out;
if (bend <= 0.001f) {
out = in;
} else if (bend >= 0.999f) {
out = std::abs(in);
} else {
float signIn = (in >= 0.f) ? 1.f : -1.f;
float absIn = std::abs(in);
float alpha = 0.1f + 0.9f * curve;
float softened = absIn / (1.f + alpha * absIn);
out = (1.f - bend) * in + bend * signIn * softened;
}
out = clamp(out, -5.f, 5.f);
outputs[OUT_OUTPUT].setVoltage(out);
}
};
struct SergeCurveBenderWidget : ModuleWidget {
SergeCurveBenderWidget(SergeCurveBender* module) {
setModule(module);
setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/SergeCurveBender.svg")));
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(10, 30)), module, SergeCurveBender::IN_INPUT));
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(10, 50)), module, SergeCurveBender::BEND_INPUT));
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(10, 70)), module, SergeCurveBender::CURVE_INPUT));
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(10, 90)), module, SergeCurveBender::DRIVE_INPUT));
addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(25, 50)), module, SergeCurveBender::BEND_PARAM));
addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(25, 70)), module, SergeCurveBender::CURVE_PARAM));
addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(25, 90)), module, SergeCurveBender::DRIVE_PARAM));
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(10, 110)), module, SergeCurveBender::OUT_OUTPUT));
}
};
Model* modelSergeCurveBender = createModel<SergeCurveBender, SergeCurveBenderWidget>("SergeCurveBender");
SVG:
<svg xmlns="http://www.w3.org/2000/svg" width="6" height="128" viewBox="0 0 6 128">
<rect width="100%" height="100%" fill="#e6f0fa"/>
<g stroke="none" fill="#fdfdf6">
<rect x="0.5" y="10" width="5" height="22" rx="1"/>
<rect x="0.5" y="38" width="5" height="22" rx="1"/>
<rect x="0.5" y="66" width="5" height="22" rx="1"/>
<rect x="0.5" y="94" width="5" height="22" rx="1"/>
</g>
<!-- Labels -->
<text x="3" y="8" text-anchor="middle" font-size="2" fill="#333">IN</text>
<text x="3" y="36" text-anchor="middle" font-size="2" fill="#333">BEND</text>
<text x="3" y="64" text-anchor="middle" font-size="2" fill="#333">CURVE</text>
<text x="3" y="92" text-anchor="middle" font-size="2" fill="#333">DRIVE</text>
<text x="3" y="124" text-anchor="middle" font-size="2" fill="#333">OUT</text>
<!-- Ports and knobs markers -->
<circle cx="1.5" cy="15" r="1" fill="#d44"/>
<circle cx="4.5" cy="15" r="1" fill="#d44"/>
<circle cx="1.5" cy="43" r="1" fill="#d44"/>
<circle cx="4.5" cy="43" r="1" fill="#d44"/>
<circle cx="1.5" cy="71" r="1" fill="#d44"/>
<circle cx="4.5" cy="71" r="1" fill="#d44"/>
<circle cx="1.5" cy="99" r="1" fill="#d44"/>
<circle cx="4.5" cy="99" r="1" fill="#d44"/>
<circle cx="3" cy="120" r="1" fill="#d44"/>
</svg>```