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: