Module expanders tutorial?

This is an abstract of what worked for me and assumes the expander is to the right of the module.

Module code:

// this is the struct we'll use to pass info back and forth
struct myExpanderMessage {
	bool somethingToGetFromTheExpander;
	int someThingToSendToTheExpander;
};


struct MyModule : Module {

	enum ParamIds {
		NUM_PARAMS
	};
	enum InputIds {
		NUM_INPUTS
	};
	enum OutputIds {
		NUM_OUTPUTS
	};
	enum LightIds {
		NUM_LIGHTS
	};
	
	// assuming the expander is on the right
	 myExpanderMessage rightMessages[2][1]; // messages to right module (expander)
	
	MyModule() {
		config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
		
		// parameter/port config goes here
		
		// set the right expander message instances
		rightExpander.producerMessage = rightMessages[0];
		rightExpander.consumerMessage = rightMessages[1];
	}

	void process(const ProcessArgs &args) override {

		// do your processing here
		
		// set up details for the expander
		if (rightExpander.module) {
			if (rightExpander.module->model == modelMyExpander) {
				
				myExpanderMessage *messageToExpander = (myExpanderMessage*)(rightExpander.module->leftExpander.producerMessage);
				myExpanderMessage *messageFromExpander = (myExpanderMessage*)(rightExpander.module->leftExpander.consumerMessage);

				messageToExpander->somethingToSendToTheExpander = someModuleValue;
				
				someExpanderValue = messageFromExpander->someThingToGetFromTheExpander;
				
				rightExpander.module->leftExpander.messageFlipRequested = true;
			}
		}

	}
};

Model *modelMyModule = createModel<MyModule, MyModuleWidget>("MyModule");

Expander code:


// this is the struct we'll use to pass info back and forth must be the same for module and expander
struct myExpanderMessage {
	bool somethingToGetFromTheExpander;
	int someThingToSendToTheExpander;
};


struct MyExpander : Module {

	enum ParamIds {
		NUM_PARAMS
	};
	
	enum InputIds {
		NUM_INPUTS
	};
	
	enum OutputIds {
		NUM_OUTPUTS
	};
	
	enum LightIds {
		NUM_LIGHTS
	};
	
	int processCount = 8;
	bool doReset = false;
	int count = 0;
	
	// assuming the controller is on the left
	myExpanderMessage leftMessages[2][1]; // messages from left module (controller module))
	
	MyExpander() {
		config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
		
		// parameter/port config goes here

		// set the left expander message instances
		leftExpander.producerMessage = leftMessages[0];
		leftExpander.consumerMessage = leftMessages[1];	
	}

	void process(const ProcessArgs &args) override {

		// do your processing here
		
		if (leftExpander.module) {
			if (leftExpander.module->model == modelMyModule) {
					
				myExpanderMessage *messagesFromModule = (myExpanderMessage *)(leftExpander.consumerMessage);
				myExpanderMessage *messagesToModule = (myExpanderMessage *)(leftExpander.producerMessage);

				someValueFromTheModule = messagesFromModule->someThingToSendToTheExpander;
				
				messagesToModule->somethingToGetFromTheExpander = someValueForTheModule;
			}
		}

	}
};

Model *modelMyExpander = createModel<MyExpander, MyExpanderWidget>("MyExpander");

In the module, you set the data what you want to send to the expander in the producerMessage and you get the expander data you want from the consumerMessage then you flip it.

In the expander, you get the module data you want from the consumerMessage and you set the data you want to send to the module in the producerMessage and it appears that you don’t need to flip it although now I’ve said that I’m sure someone far cleverer than I am will correct me.

I tested this with a dummy expander on a sequencer module with the sequecner passing the current step to the expander for display and the expander passing a button value back to the sequencer to make it manually reset and it worked very nicely:

image

8 Likes