Any expander tutorial around?

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!

2 Likes