Skip to content

Commit

Permalink
Add user options for Send Change Threshold
Browse files Browse the repository at this point in the history
Add context menu options for users to change the send change threshold. Each module has its own setting (applied to all channels it owns).
  • Loading branch information
chichian committed Mar 4, 2023
1 parent 0880eb3 commit c6abd85
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 12 deletions.
19 changes: 15 additions & 4 deletions src/Module_oscCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ void oscCV::onReset() {

sendDt = 0.0f;
sendFrequency_Hz = TROWA_OSCCV_DEFAULT_SEND_HZ;
sendChangeSensitivity = TROWA_OSCCV_DEFAULT_CHANGE_THRESHOLD;
return;
} // end reset()
//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
Expand Down Expand Up @@ -351,7 +352,8 @@ json_t *oscCV::dataToJson() {
json_object_set_new(oscJ, "Namespace", json_string(this->oscNamespace.c_str()));
json_object_set_new(oscJ, "AutoReconnectAtLoad", json_boolean(oscReconnectAtLoad)); // [v11, v0.6.3]
json_object_set_new(oscJ, "Initialized", json_boolean(oscInitialized)); // [v11, v0.6.3] We know the settings are good at least at the time of save
json_object_set_new(oscJ, "SendFrequency", json_integer(sendFrequency_Hz)); // [v19: 2.0.5] Users can change the Send Frequency now
json_object_set_new(oscJ, "SendFrequency", json_integer(sendFrequency_Hz)); // [v19: 2.0.5] Users can change the Send Frequency now
json_object_set_new(oscJ, "SendChangeSensitivity", json_real(sendChangeSensitivity)); // [v22: 2.0.8] Users can change the channel sensitivity now.
json_object_set_new(rootJ, "osc", oscJ);

// Channels
Expand Down Expand Up @@ -405,6 +407,14 @@ void oscCV::dataFromJson(json_t *rootJ) {
currJ = json_object_get(oscJ, "SendFrequency");
if (currJ)
this->sendFrequency_Hz = (int)(json_integer_value(currJ));

// [v22: 2.0.8] Users can change the channel sensitivity now.
// If users do not have a trigger, then the change needed to send.
currJ = json_object_get(oscJ, "SendChangeSensitivity");
if (currJ)
this->sendChangeSensitivity = (float)(json_real_value(currJ));
else
this->sendChangeSensitivity = TROWA_OSCCV_DEFAULT_CHANGE_THRESHOLD;
} // end if OSC node

// Channels
Expand Down Expand Up @@ -632,7 +642,8 @@ void oscCV::process(const ProcessArgs &args)
if (!inputChannels[c].doSend) {
// Only send if changed enough. Maybe about 0.01 V? Defined on channel.
// 1. Check for change:
sendVal = inputChannels[c].valChanged();
// Currently all channels have the same sensitivity, one day maybe we have this settable by channel.
sendVal = inputChannels[c].valChanged(this->sendChangeSensitivity);
// 2. Mark channel as needing to output
if (sendVal) {
inputChannels[c].doSend = true;
Expand Down Expand Up @@ -724,8 +735,8 @@ void oscCV::process(const ProcessArgs &args)
Expander* exp = &(this->leftExpander);
while (exp != NULL && exp->module
&& CVOSCCV_IS_EXPANDER_INPUT_MODEL(exp->module->model)) // == modelOscCVExpanderInput)
{
dynamic_cast<oscCVExpander*>(exp->module)->processInputs(oscNamespace, oscInitialized, sendTime, packetOpened, oscMutex, oscStream);
{
dynamic_cast<oscCVExpander*>(exp->module)->processInputs(oscNamespace, oscInitialized, sendTime, sendChangeSensitivity, packetOpened, oscMutex, oscStream);
exp = &(exp->module->leftExpander); // Go to next so we can see if that's another expander.
}
}
Expand Down
39 changes: 39 additions & 0 deletions src/Module_oscCV.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ struct oscCV : Module {

float sendDt = 0.0f;
int sendFrequency_Hz = TROWA_OSCCV_DEFAULT_SEND_HZ;
float sendChangeSensitivity = TROWA_OSCCV_DEFAULT_CHANGE_THRESHOLD;

// Flag to reconnect at load. IFF true and oscInitialized is also true.
bool oscReconnectAtLoad = false;
Expand Down Expand Up @@ -427,6 +428,44 @@ struct oscCV : Module {
sendFrequency_Hz = TROWA_OSCCV_Send_Freq_Opts_Hz[ix];
return;
}

//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// getSendChangeThresholdIx()
// Gets the index into the global TROWA_OSCCV_Change_Threshold_Opts array that matches the
// send change frequency.
// (QUICK and dirty: Options list instead of making a text box).
//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
int getSendChangeThresholdIx() {
int ix = -1;
int i = 0;
while (i < TROWA_OSCCV_NUM_CHANGE_OPTS) {
if (sendChangeSensitivity == TROWA_OSCCV_Change_Threshold_Opts[i])
{
ix = i;
i = TROWA_OSCCV_NUM_CHANGE_OPTS;
}
i++;
}
if (ix < 0)
ix = 0; // Just pick first option
return ix;
}
//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// setSendChangeThresholdIx()
// @ix: (IN) Index into the TROWA_OSCCV_Change_Threshold_Opts array of the threshold to use.
// Sets the send change threshold based off the given index into the global TROWA_OSCCV_Change_Threshold_Opts
// array.
// (QUICK and dirty: Options list instead of making a text box).
//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
void setSendChangeThresholdIx(int ix) {
if (ix < 0)
ix = 0;
else if (ix > TROWA_OSCCV_NUM_CHANGE_OPTS - 1)
ix = TROWA_OSCCV_NUM_CHANGE_OPTS - 1;
sendChangeSensitivity = TROWA_OSCCV_Change_Threshold_Opts[ix];
return;
}


#if USE_MODULE_STATIC_RX
//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
Expand Down
19 changes: 17 additions & 2 deletions src/Module_oscCVExpander.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ void oscCVExpander::onReset() {
this->lvlFromMaster = findMaster(0, baseChannels, masterModuleId);
if (lvlFromMaster == -1)
baseChannels = TROWA_OSCCV_DEFAULT_NUM_CHANNELS; // Master probably has this many channels.
this->sendChangeSensitivity = TROWA_OSCCV_CHANGE_THRESHHOLD_USE_PARENT;
oscMutex.lock();
initChannels(baseChannels);
oscMutex.unlock();
Expand Down Expand Up @@ -393,7 +394,7 @@ void oscCVExpander::process(const ProcessArgs &args)
// processInputs()
// Process CV->OSC.
//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
void oscCVExpander::processInputs(std::string oscNamespace, bool oscInitialized, bool sendTime, bool& packetOpened, std::mutex& sendMutex, osc::OutboundPacketStream& oscStream)
void oscCVExpander::processInputs(std::string oscNamespace, bool oscInitialized, bool sendTime, float changeSensitivity, bool& packetOpened, std::mutex& sendMutex, osc::OutboundPacketStream& oscStream)
{
/// TODO: If we make some osc base class, this could be built-in for oscCV and these expanders.
try
Expand All @@ -402,6 +403,10 @@ void oscCVExpander::processInputs(std::string oscNamespace, bool oscInitialized,
{
// Read the channels and output to OSC
char addressBuffer[512];

// Use the parent/master change sensitivity given to us if we are set to negative.
float changeThreshold = (this->sendChangeSensitivity < 0.0f) ? changeSensitivity : this->sendChangeSensitivity;

for (int c = 0; c < this->numberChannels; c++)
{
bool sendVal = false;
Expand Down Expand Up @@ -431,7 +436,8 @@ void oscCVExpander::processInputs(std::string oscNamespace, bool oscInitialized,
if (!inputChannels[c].doSend) {
// Only send if changed enough. Maybe about 0.01 V? Defined on channel.
// 1. Check for change:
sendVal = inputChannels[c].valChanged();
// Currently all channels have the same sensitivity, one day maybe we have this settable by channel.
sendVal = inputChannels[c].valChanged(changeThreshold);
// 2. Mark channel as needing to output
if (sendVal) {
inputChannels[c].doSend = true;
Expand Down Expand Up @@ -648,6 +654,7 @@ json_t *oscCVExpander::dataToJson() {
json_object_set_new(rootJ, "type", json_integer((int)expanderType));
json_object_set_new(rootJ, "expId", json_string(this->_expID.c_str()));
json_object_set_new(rootJ, "displayName", json_string(this->displayName.c_str()));
json_object_set_new(rootJ, "sendChangeSensitivity", json_real(sendChangeSensitivity)); // [v22: 2.0.8] Users can change the channel sensitivity now.


// Channels
Expand Down Expand Up @@ -681,6 +688,14 @@ void oscCVExpander::dataFromJson(json_t *rootJ) {
currJ = json_object_get(rootJ, "displayName");
if (currJ)
this->displayName = json_string_value(currJ);
// [v22: 2.0.8] Users can change the channel sensitivity now.
// If users do not have a trigger, then the change needed to send.
currJ = json_object_get(rootJ, "sendChangeSensitivity");
if (currJ)
this->sendChangeSensitivity = (float)(json_real_value(currJ));
else
this->sendChangeSensitivity = TROWA_OSCCV_CHANGE_THRESHHOLD_USE_PARENT;


// Channels
int nChannels = numberChannels;
Expand Down
46 changes: 45 additions & 1 deletion src/Module_oscCVExpander.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,13 @@ struct oscCVExpander : Module
// // Left message. If this is OUTPUT, then a master should be on the left.
// TSOSCCVExpanderMessage* leftMessage[2];


// Change sensitivity for this module. Only applies to expanders that send, but
// maybe we'll have an expander with both inputs & outputs one day, so we'll throw it in the base class for now.
// If TROWA_OSCCV_CHANGE_THRESHHOLD_USE_PARENT ( a negative value), then use the parent's.
float sendChangeSensitivity = TROWA_OSCCV_CHANGE_THRESHHOLD_USE_PARENT;


//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// oscCVExpander()
// Create a module with numChannels.
Expand Down Expand Up @@ -224,6 +231,43 @@ struct oscCVExpander : Module
int baseChannels = 0;
this->lvlFromMaster = findMaster(0, baseChannels, masterModuleId);
}

//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// getSendChangeThresholdIx()
// Gets the index into the global TROWA_OSCCV_Change_Threshold_Opts array that matches the
// send change frequency.
// (QUICK and dirty: Options list instead of making a text box).
//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
int getSendChangeThresholdIx() {
int ix = -1;
int i = 0;
while (i < TROWA_OSCCV_NUM_CHANGE_OPTS) {
if (sendChangeSensitivity == TROWA_OSCCV_Change_Threshold_Opts[i])
{
ix = i;
i = TROWA_OSCCV_NUM_CHANGE_OPTS;
}
i++;
}
if (ix < 0)
ix = 0; // Just pick first option
return ix;
}
//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// setSendChangeThresholdIx()
// @ix: (IN) Index into the TROWA_OSCCV_Change_Threshold_Opts array of the threshold to use.
// Sets the send change threshold based off the given index into the global TROWA_OSCCV_Change_Threshold_Opts
// array.
// (QUICK and dirty: Options list instead of making a text box).
//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
void setSendChangeThresholdIx(int ix) {
if (ix < 0)
ix = 0;
else if (ix > TROWA_OSCCV_NUM_CHANGE_OPTS - 1)
ix = TROWA_OSCCV_NUM_CHANGE_OPTS - 1;
sendChangeSensitivity = TROWA_OSCCV_Change_Threshold_Opts[ix];
return;
}

//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// process()
Expand All @@ -233,7 +277,7 @@ struct oscCVExpander : Module
// processInputs()
// Process CV->OSC.
//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
void processInputs(std::string oscNamespace, bool oscInitialized, bool sendTime, bool& packetOpened, std::mutex& sendMutex, osc::OutboundPacketStream& oscStream);
void processInputs(std::string oscNamespace, bool oscInitialized, bool sendTime, float changeSensitivity, bool& packetOpened, std::mutex& sendMutex, osc::OutboundPacketStream& oscStream);
//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// processOutputs()
// Process OSC->CV (from msg queue).
Expand Down
3 changes: 3 additions & 0 deletions src/TSOSCCV_Common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
// Add in some frequencies to match up with FPS.
const int TROWA_OSCCV_Send_Freq_Opts_Hz[TROWA_OSCCV_NUM_SEND_HZ_OPTS] = { 100, 120, 240, 300, 500, 1000 };

// Available options for change threshold (V or whatever the translated value is).
const float TROWA_OSCCV_Change_Threshold_Opts[TROWA_OSCCV_NUM_CHANGE_OPTS] = { 0.0001f, 0.0005f, 0.001f, 0.005f, 0.01f, 0.05f };

//--------------------------------------------------------
// addValToBuffer()
// Add a value to the buffer.
Expand Down
36 changes: 32 additions & 4 deletions src/TSOSCCV_Common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ using namespace rack;

#define TROWA_OSCCV_VECTOR_MAX_SIZE engine::PORT_MAX_CHANNELS // Now with polyphonic cables, there can be 16 channels sent in one CV input/output


//=== DEBUG MacOS ====
// for cvOSCcv
#define DEBUG_MAC_OS_POINTER 0
Expand All @@ -40,6 +41,13 @@ using namespace rack;
#define TROWA_OSCCV_NUM_SEND_HZ_OPTS 6 // Number of send options in our simple array. Add quick & dirty simple run-time config of sending frequency.
extern const int TROWA_OSCCV_Send_Freq_Opts_Hz[TROWA_OSCCV_NUM_SEND_HZ_OPTS];


#define TROWA_OSCCV_DEFAULT_CHANGE_THRESHOLD 0.05f // Default change threshold for sending when no trigger is present
#define TROWA_OSCCV_NUM_CHANGE_OPTS 6
// Options for change threshold.
extern const float TROWA_OSCCV_Change_Threshold_Opts[TROWA_OSCCV_NUM_CHANGE_OPTS];
#define TROWA_OSCCV_CHANGE_THRESHHOLD_USE_PARENT -100.f // Value to signal to use the parent's (master's) change threshold for cvOSC expanders.

// A channel for OSC.
struct TSOSCCVChannel {
// Base param ids for the channel
Expand Down Expand Up @@ -328,9 +336,10 @@ struct TSOSCCVInputChannel : TSOSCCVChannel {
std::vector<float> lastVals;
// The last translated value we SENT over OSC (for tracking changes).
std::vector<float> lastTranslatedVals;

// If trigger is not set up (input type channel), how much input change is needed to send a message out.
float channelSensitivity = 0.05f;
float channelSensitivity = TROWA_OSCCV_DEFAULT_CHANGE_THRESHOLD;

// If we should send. Working value for module.
bool doSend = false;

Expand All @@ -353,7 +362,7 @@ struct TSOSCCVInputChannel : TSOSCCVChannel {
}
void initialize() override {
initLastVals();
channelSensitivity = 0.05f;
channelSensitivity = TROWA_OSCCV_DEFAULT_CHANGE_THRESHOLD;
TSOSCCVChannel::initialize();
doSend = false;
return;
Expand Down Expand Up @@ -386,7 +395,26 @@ struct TSOSCCVInputChannel : TSOSCCVChannel {
}
return sendVal;
}

// Checks to see if vals have changed enough to send a value.
bool valChanged(float threshold)
{
bool sendVal = false;
int i = 0;
while (i < numVals && !sendVal)
{
if (convertVals)
{
sendVal = std::abs(translatedVals[i] - lastTranslatedVals[i]) > threshold;
}
else
{
sendVal = std::abs(vals[i] - lastVals[i]) > threshold;
}
i++;
}
return sendVal;
}

void storeLastValues()
{
for (int i = 0; i < TROWA_OSCCV_VECTOR_MAX_SIZE; i++)
Expand Down
15 changes: 15 additions & 0 deletions src/Widget_oscCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1115,6 +1115,21 @@ void oscCVWidget::appendContextMenu(ui::Menu *menu)
}
));

std::vector<std::string> optLabels2;
for (int i = 0; i < TROWA_OSCCV_NUM_CHANGE_OPTS; i++) {
optLabels2.push_back(rack::string::f("%.4f", TROWA_OSCCV_Change_Threshold_Opts[i]));
}
menu->addChild(createIndexSubmenuItem("Change Threshold",
optLabels2,
[=]() {
return thisModule->getSendChangeThresholdIx();
},
[=](int ix) {
thisModule->setSendChangeThresholdIx(ix);
}
));


return;
}

Expand Down
8 changes: 7 additions & 1 deletion src/Widget_oscCVExpander.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#include <vector>

#include "Widget_oscCVExpander.hpp"
#include <rack.hpp>
using namespace rack;

#include "trowaSoft.hpp"
#include "trowaSoftComponents.hpp"
#include "trowaSoftUtilities.hpp"
Expand Down Expand Up @@ -619,4 +622,7 @@ void TSOscCVExpanderLabels::draw(/*in*/ const DrawArgs &args) {
y = box.size.y - 13;
nvgText(args.vg, x, y, labelType, NULL);
return;
} // end TSOscCVExpanderLabels::draw()
} // end TSOscCVExpanderLabels::draw()



29 changes: 29 additions & 0 deletions src/Widget_oscCVExpander.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,35 @@ struct oscCVExpanderInputWidget : oscCVExpanderWidget {
{
return;
}

// Append custom context menu options (for the send threshold).
void appendContextMenu(ui::Menu* menu) override {
oscCVExpander* thisModule = dynamic_cast<oscCVExpander*>(this->module);
if (thisModule)
{
menu->addChild(new MenuSeparator);

std::vector<std::string> optLabels2;
optLabels2.push_back(std::string("Match Master"));
for (int i = 0; i < TROWA_OSCCV_NUM_CHANGE_OPTS; i++) {
optLabels2.push_back(rack::string::f("%.4f", TROWA_OSCCV_Change_Threshold_Opts[i]));
}
menu->addChild(createIndexSubmenuItem("Change Threshold",
optLabels2,
[=]() {
// Extra one for the use parent option
return (thisModule->sendChangeSensitivity < 0.f) ? 0 : thisModule->getSendChangeThresholdIx() + 1;
},
[=](int ix) {
if (ix > 0)
thisModule->setSendChangeThresholdIx(ix - 1);
else
thisModule->sendChangeSensitivity = TROWA_OSCCV_CHANGE_THRESHHOLD_USE_PARENT;
}
));
}
return;
}
};
//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// oscCVExpanderOutputWidget
Expand Down
Loading

0 comments on commit c6abd85

Please sign in to comment.