For all the lazy people searching for their first ever expander project in the future I leave here a simple and short code (< 100 lines) as demo.
// A short demo for a simple module (DemoExpR) and an expander (DemoExpRModule)
#include "plugin.hpp"
struct DemoExpR : Module {
enum ParamId {KNOB_PARAM, PARAMS_LEN};
enum InputId {INPUTS_LEN};
enum OutputId {CV_OUTPUT, OUTPUTS_LEN};
enum LightId {LIGHTS_LEN};
DemoExpR() {
config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN);
configParam(KNOB_PARAM, 0.0f, 1.0f, 0.5f, "Set voltage");
configOutput(CV_OUTPUT, "Voltage");
}
float newVolt;
void process(const ProcessArgs& args) override {
newVolt=params[KNOB_PARAM].getValue();
outputs[CV_OUTPUT].setVoltage(newVolt);
}
};
struct DemoExpRWidget : ModuleWidget {
DemoExpR* module;
DemoExpRWidget(DemoExpR* module) {
this->module = module;
setModule(module);
setPanel(createPanel(asset::plugin(pluginInstance, "res/DemoExpR.svg")));
addParam(createParamCentered<RoundSmallBlackKnob>(mm2px(Vec(5.08, 15.24)), module, DemoExpR::KNOB_PARAM));
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(5.08, 30.48)), module, DemoExpR::CV_OUTPUT));
}
};
Model* modelDemoExpR = createModel<DemoExpR, DemoExpRWidget>("DemoExpR");
/**************************************************/
// EXPANDER CODE - starting here
/**************************************************/
#include "plugin.hpp"
struct DemoExpRModule : Module {
enum ParamId {PARAMS_LEN};
enum InputId {INPUTS_LEN};
enum OutputId {CV_INVERT_OUTPUT,OUTPUTS_LEN};
enum LightId {LIGHTS_LEN};
DemoExpRModule() {
config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN);
configOutput(CV_INVERT_OUTPUT, "CV invert");
}
DemoExpR* findHostModulePtr(Module* module) {
if (module) {
if (module->leftExpander.module) {
// if it's the mother module, we're done
if (module->leftExpander.module->model == modelDemoExpR) {
return reinterpret_cast<DemoExpR*>(module->leftExpander.module);
}
}
}
return nullptr;
}
void process(const ProcessArgs& args) override {
DemoExpR const* mother = findHostModulePtr(this);
if (mother) {outputs[CV_INVERT_OUTPUT].setVoltage(mother->newVolt*-1);}
else {outputs[CV_INVERT_OUTPUT].setVoltage(-0.404);}
}
};
struct DemoExpRModuleWidget : ModuleWidget {
DemoExpRModule* module;
DemoExpRModuleWidget(DemoExpRModule* module) {
this->module = module;
setModule(module);
setPanel(createPanel(asset::plugin(pluginInstance, "res/DemoExpR.svg")));
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(5.08, 30.48)), module, DemoExpRModule::CV_INVERT_OUTPUT));
}
};
Model* modelDemoExpRModule = createModel<DemoExpRModule, DemoExpRModuleWidget>("DemoExpRModule");
This code creates two modules. One of them (DemoExpR) is the mother with an output and a knob. The other one (DemoExpRModule) is the expander reading mother’s knob value and sending it to it’s own output.
It’s a one-way communication here. It probably can’t be simpler than this. For more options and features study the VCV API!