Cool! I got my own code working before I saw you posting. Here’s my version:
First, I have two different structures for the data being passed, depending if it’s being sent from the module to expander, or from the expander to the module.
#pragma once
struct ExpanderToGrooveboxMessage
{
bool message_received = true;
bool mutes[8];
bool solos[8];
float track_volumes[8];
ExpanderToGrooveboxMessage()
{
for(unsigned int i=0; i<8; i++)
{
mutes[i] = false;
solos[i] = false;
track_volumes[i] = 1.0;
}
}
};
and…
#pragma once
struct GrooveboxToExpanderMessage
{
bool track_triggers[8];
bool message_received = true;
GrooveboxToExpanderMessage()
{
for(unsigned int i=0; i<8; i++)
{
track_triggers[i] = false;
}
}
};
Next, I had to make myself a little diagram to keep my brain from exploding:
Let’s start with the message that goes from the
groove box expander => groovebox
Step #1:
In GrooveBox, I created two message containers:
struct GrooveBox : Module
{
ExpanderToGrooveboxMessage expander_to_groovebox_message_a;
ExpanderToGrooveboxMessage expander_to_groovebox_message_b;
Step #2:
In the groovebox constructor, I set the following:
GrooveBox()
{
this->leftExpander.producerMessage = &expander_to_groovebox_message_a;
this->leftExpander.consumerMessage = &expander_to_groovebox_message_b;
Step #3:
In GrooveBoxExpander.hpp (the expander’s main code), I added the following function which is called whenever it’s time to send information from the expander to the module. I check to see if anything has changed before calling this function to avoid unnecessary work:
void writeToGroovebox()
{
if (rightExpander.module && rightExpander.module->model == modelGrooveBox)
{
// Prepare message for sending to Grain Engine MK2
// When writing to the groovebox, we're using the __GrooveBox's__ producer and consumer pair
// Always write to the producer and read from the consumer
ExpanderToGrooveboxMessage *message_to_groove_box = (ExpanderToGrooveboxMessage *) rightExpander.module->leftExpander.producerMessage;
// Wait until the groovebox received the last message
if(message_to_groove_box && message_to_groove_box->message_received == true)
{
for(unsigned int i=0; i < NUMBER_OF_TRACKS; i++)
{
message_to_groove_box->mutes[i] = mutes[i];
message_to_groove_box->solos[i] = solos[i];
message_to_groove_box->track_volumes[i] = params[VOLUME_KNOBS + i].getValue();
}
// Tell GrooveBox that the message is ready for receiving
message_to_groove_box->message_received = false;
}
}
}
Notice that there’s no “flip” request. I have the receiving code ask for the flip.
Step #4:
In the GrooveBox module’s code, I wrote the following function to receive the messages from the expander. This function is called from the GrooveBox’s Process loop:
void readFromExpander(float rack_sample_rate)
{
if (leftExpander.module && leftExpander.module->model == modelGrooveBoxExpander)
{
// Receive message from expander. Always read from the consumer.
// when reading from the expander, we're using the __GrooveBox's__ consumer and producer message pair
ExpanderToGrooveboxMessage *consumer_message = (ExpanderToGrooveboxMessage *) leftExpander.consumerMessage;
// Retrieve the data from the expander
if(consumer_message && consumer_message->message_received == false)
{
this->any_track_soloed = false;
for(unsigned int i=0; i < NUMBER_OF_TRACKS; i++)
{
bool expander_mute_value = consumer_message->mutes[i];
bool expander_solo_value = consumer_message->solos[i];
// THIS IS ALL MY IMPLEMENTATION SPECIFIC
// ======================================
if((this->mutes[i] == false) && (expander_mute_value == true) && (this->solos[i] == false))
{
this->selected_memory_slot->tracks[i].fadeOut(rack_sample_rate);
}
this->mutes[i] = expander_mute_value;
this->solos[i] = expander_solo_value;
this->track_volumes[i] = consumer_message->track_volumes[i];
if(this->solos[i]) this->any_track_soloed = true;
// ======================================
}
// Set the received flag
consumer_message->message_received = true;
}
leftExpander.messageFlipRequested = true;
}
}
groove => groovebox box expander
This is essentially the opposite!
Step #1:
In the GrooveBox Expander, I added the following variables:
struct GrooveBoxExpander : Module
{
GrooveboxToExpanderMessage groovebox_to_expander_message_a;
GrooveboxToExpanderMessage groovebox_to_expander_message_b;
Step #2:
In the GrooveBox Expander constructor:
GrooveBoxExpander()
{
rightExpander.producerMessage = &groovebox_to_expander_message_a;
rightExpander.consumerMessage = &groovebox_to_expander_message_b;
Step #3: In the main Groovebox module, I added code for sending the data to the expander. This is called from the main module’s Process loop:
void writeToExpander()
{
if (leftExpander.module && leftExpander.module->model == modelGrooveBoxExpander)
{
// Always write to the producerMessage
GrooveboxToExpanderMessage *groovebox_to_expander_message = (GrooveboxToExpanderMessage *) leftExpander.module->rightExpander.producerMessage;
if(groovebox_to_expander_message && groovebox_to_expander_message->message_received == true)
{
for(unsigned int i=0; i < NUMBER_OF_TRACKS; i++)
{
groovebox_to_expander_message->track_triggers[i] = this->track_triggers[i];
if(this->track_triggers[i]) this->track_triggers[i] = false;
}
groovebox_to_expander_message->message_received = false;
}
}
}
(Notice again that there’s no “flip” request in the sending code. I put that in the receiving code.)
Step #4
Here’s the code in the Expander that receives the information. This is called from the expander’s Process() loop:
void readFromGroovebox()
{
if (rightExpander.module && rightExpander.module->model == modelGrooveBox)
{
// Receive message from expander
GrooveboxToExpanderMessage *groovebox_message = (GrooveboxToExpanderMessage *) this->rightExpander.consumerMessage;
// Retrieve the data from the expander
if(groovebox_message && groovebox_message->message_received == false)
{
for(unsigned int i=0; i < NUMBER_OF_TRACKS; i++)
{
if(groovebox_message->track_triggers[i])
{
// Trigger output
triggerOutputPulseGenerators[i].trigger(0.01f);
triggerLightPulseGenerators[i].trigger(0.05f);
}
}
groovebox_message->message_received = true;
}
rightExpander.messageFlipRequested = true;
}
}