diff --git a/resources/surge-shared/oscspecification.html b/resources/surge-shared/oscspecification.html
index 72b65ee2ef0..56cca6dc2cb 100644
--- a/resources/surge-shared/oscspecification.html
+++ b/resources/surge-shared/oscspecification.html
@@ -888,9 +888,9 @@
Replace '<s>' with 'a' or 'b' (scene) and <n> with '1' through '
Appropriate Values |
- /param/<s>/osc/<n>/keytrack |
- Osc <n> Keytrack |
- boolean (0 or 1) |
+ /param/<s>/osc/<n>/type |
+ Osc <n> Type |
+ integer (0 to 11) |
/param/<s>/osc/<n>/octave |
@@ -898,55 +898,55 @@ Replace '<s>' with 'a' or 'b' (scene) and <n> with '1' through '
integer (-3 to 3) |
+ /param/<s>/osc/<n>/keytrack |
+ Osc <n> Keytrack |
+ boolean (0 or 1) |
+
+
+ /param/<s>/osc/<n>/pitch |
+ Osc <n> Pitch |
+ float (0.0 to 1.0) |
+
+
+ /param/<s>/osc/<n>/retrigger |
+ Osc <n> Retrigger |
+ boolean (0 or 1) |
+
+
/param/<s>/osc/<n>/param1 |
- Osc <n> Morph |
+ Osc <n> Param 1 |
(contextual) |
/param/<s>/osc/<n>/param2 |
- Osc <n> Skew Vertical |
+ Osc <n> Param 2 |
(contextual) |
/param/<s>/osc/<n>/param3 |
- Osc <n> Saturate |
+ Osc <n> Param 3 |
(contextual) |
/param/<s>/osc/<n>/param4 |
- Osc <n> Formant |
+ Osc <n> Param 4 |
(contextual) |
/param/<s>/osc/<n>/param5 |
- Osc <n> Skew Horizontal |
+ Osc <n> Param 5 |
(contextual) |
/param/<s>/osc/<n>/param6 |
- Osc <n> Unison Detune |
+ Osc <n> Param 6 |
(contextual) |
/param/<s>/osc/<n>/param7 |
- Osc <n> Unison Voices |
+ Osc <n> Param 7 |
(contextual) |
-
- /param/<s>/osc/<n>/pitch |
- Osc <n> Pitch |
- float (0.0 to 1.0) |
-
-
- /param/<s>/osc/<n>/retrigger |
- Osc <n> Retrigger |
- boolean (0 or 1) |
-
-
- /param/<s>/osc/<n>/type |
- Osc <n> Type |
- integer (0 to 11) |
-
diff --git a/src/common/SurgeSynthesizer.cpp b/src/common/SurgeSynthesizer.cpp
index f11bcafb14e..6f2f0d3136e 100644
--- a/src/common/SurgeSynthesizer.cpp
+++ b/src/common/SurgeSynthesizer.cpp
@@ -2404,7 +2404,7 @@ void SurgeSynthesizer::channelController(char channel, int cc, int value)
// Notify audio thread param change listeners (OSC, e.g.)
// (which run on juce messenger thread)
for (const auto &it : audioThreadParamListeners)
- (it.second)(storage.getPatch().param_ptr[i]->oscName, fval);
+ (it.second)(storage.getPatch().param_ptr[i]->oscName, fval, "");
int j = 0;
while (j < 7)
@@ -3196,50 +3196,64 @@ bool SurgeSynthesizer::loadOscalgos()
for (int i = 0; i < n_oscs; i++)
{
localResendOscParams[s][i] = false;
- if (storage.getPatch().scene[s].osc[i].queue_type > -1)
+ auto &osc_st = storage.getPatch().scene[s].osc[i];
+ if (osc_st.queue_type > -1)
{
algosChanged = true;
- // clear assigned modulation if we change osc type, see issue #2224
- if (storage.getPatch().scene[s].osc[i].queue_type !=
- storage.getPatch().scene[s].osc[i].type.val.i)
+ // clear assigned modulation, and echo to OSC if we change osc type, see issue #2224
+ if (osc_st.queue_type != osc_st.type.val.i)
{
clear_osc_modulation(s, i);
}
- storage.getPatch().scene[s].osc[i].type.val.i =
- storage.getPatch().scene[s].osc[i].queue_type;
- storage.getPatch().update_controls(false, &storage.getPatch().scene[s].osc[i]);
- storage.getPatch().scene[s].osc[i].queue_type = -1;
+ // Notify audio thread param change listeners (OSC, e.g.)
+ // (which run on juce messenger thread)
+ std::stringstream sb;
+ sb << "/param/" << (char)('a' + s) << "/osc/" << i + 1 << "/type";
+ auto new_type = osc_st.queue_type;
+ for (const auto &it : audioThreadParamListeners)
+ (it.second)(sb.str(), new_type, osc_type_names[new_type]);
+
+ osc_st.type.val.i = osc_st.queue_type;
+ storage.getPatch().update_controls(false, &osc_st);
+ osc_st.queue_type = -1;
switch_toggled_queued = true;
refresh_editor = true;
localResendOscParams[s][i] = true;
}
- TiXmlElement *e = (TiXmlElement *)storage.getPatch().scene[s].osc[i].queue_xmldata;
+ TiXmlElement *e = (TiXmlElement *)osc_st.queue_xmldata;
if (e)
{
storage.getPatch().isDirty = true;
for (int k = 0; k < n_osc_params; k++)
{
+ std::string oname = osc_st.p[k].oscName;
+ std::string sx = osc_st.p[k].get_name();
+
double d;
int j;
std::string lbl;
lbl = fmt::format("p{:d}", k);
- if (storage.getPatch().scene[s].osc[i].p[k].valtype == vt_float)
+ if (osc_st.p[k].valtype == vt_float)
{
if (e->QueryDoubleAttribute(lbl.c_str(), &d) == TIXML_SUCCESS)
{
- storage.getPatch().scene[s].osc[i].p[k].val.f = (float)d;
+ osc_st.p[k].val.f = (float)d;
+ for (const auto &it : audioThreadParamListeners)
+ (it.second)(oname, d, sx);
}
}
else
{
if (e->QueryIntAttribute(lbl.c_str(), &j) == TIXML_SUCCESS)
{
- storage.getPatch().scene[s].osc[i].p[k].val.i = j;
+ osc_st.p[k].val.i = j;
+ for (const auto &it : audioThreadParamListeners)
+ (it.second)(oname, j, sx);
}
}
@@ -3247,31 +3261,40 @@ bool SurgeSynthesizer::loadOscalgos()
if (e->QueryIntAttribute(lbl.c_str(), &j) == TIXML_SUCCESS)
{
- storage.getPatch().scene[s].osc[i].p[k].deform_type = j;
+ osc_st.p[k].deform_type = j;
+ for (const auto &it : audioThreadParamListeners)
+ (it.second)(oname, j, sx);
}
lbl = fmt::format("p{:d}_extend_range", k);
if (e->QueryIntAttribute(lbl.c_str(), &j) == TIXML_SUCCESS)
{
- storage.getPatch().scene[s].osc[i].p[k].set_extend_range(j);
+ osc_st.p[k].set_extend_range(j);
+ for (const auto &it : audioThreadParamListeners)
+ (it.second)(oname, j, sx);
}
}
int rt;
if (e->QueryIntAttribute("retrigger", &rt) == TIXML_SUCCESS)
{
- storage.getPatch().scene[s].osc[i].retrigger.val.b = rt;
+ osc_st.retrigger.val.b = rt;
+ std::stringstream sb;
+ sb << "/param/" << (char)('a' + s) << "/osc/" << i + 1 << "/retrigger";
+ std::string sx = rt > 0 ? "On" : "Off";
+ for (const auto &it : audioThreadParamListeners)
+ (it.second)(sb.str(), rt, sx);
}
/*
* Some oscillator types can change display when you change values
*/
- if (storage.getPatch().scene[s].osc[i].type.val.i == ot_modern)
+ if (osc_st.type.val.i == ot_modern)
{
refresh_editor = true;
}
- storage.getPatch().scene[s].osc[i].queue_xmldata = 0;
+ osc_st.queue_xmldata = 0;
}
}
}
diff --git a/src/common/SurgeSynthesizer.h b/src/common/SurgeSynthesizer.h
index 512ea99d46a..6adcadde8f2 100644
--- a/src/common/SurgeSynthesizer.h
+++ b/src/common/SurgeSynthesizer.h
@@ -461,13 +461,13 @@ class alignas(16) SurgeSynthesizer
//==============================================================================
// Parameter changes coming from within the synth (e.g. from MIDI-learned input)
// are communicated to listeners here
- std::unordered_map>
+ std::unordered_map>
audioThreadParamListeners;
- void
- addAudioParamListener(std::string key,
- std::function const &l)
+ void addAudioParamListener(std::string key,
+ std::function const &l)
{
audioThreadParamListeners.insert({key, l});
}
diff --git a/src/surge-xt/osc/OpenSoundControl.cpp b/src/surge-xt/osc/OpenSoundControl.cpp
index 9dfe1e26660..96bc3c656a4 100644
--- a/src/surge-xt/osc/OpenSoundControl.cpp
+++ b/src/surge-xt/osc/OpenSoundControl.cpp
@@ -1282,20 +1282,22 @@ bool OpenSoundControl::initOSCOut(int port, std::string ipaddr)
// Add a listener for parameter changes that happen on the audio thread
// (e.g. MIDI-'learned' parameters being changed by incoming MIDI messages)
- synth->addAudioParamListener("OSC_OUT", [this, ssp = sspPtr](std::string oname, float fval) {
- assert(juce::MessageManager::getInstanceWithoutCreating());
- auto *mm = juce::MessageManager::getInstanceWithoutCreating();
- if (mm)
- {
- mm->callAsync(
- [ssp, oname, fval]() { ssp->param_change_to_OSC(oname, 1, fval, 0., 0., ""); });
- }
- else
- {
- std::cerr << "The juce message manager is not running. You are misconfigured"
- << std::endl;
- }
- });
+ synth->addAudioParamListener(
+ "OSC_OUT", [this, ssp = sspPtr](std::string oname, float fval, std::string valstr) {
+ assert(juce::MessageManager::getInstanceWithoutCreating());
+ auto *mm = juce::MessageManager::getInstanceWithoutCreating();
+ if (mm)
+ {
+ mm->callAsync([ssp, oname, fval, valstr]() {
+ ssp->param_change_to_OSC(oname, 1, fval, 0., 0., valstr);
+ });
+ }
+ else
+ {
+ std::cerr << "The juce message manager is not running. You are misconfigured"
+ << std::endl;
+ }
+ });
// Add a listener for modulation changes
synth->addModulationAPIListener(this);