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);