commit 07b9a3adf4612bc368f803aa90a987a6e18c6ece
parent f8afad6833b019be46b384034fc2f91733403055
Author: Johannes Lorenz <j.git@lorenz-ho.me>
Date: Sun, 26 Jan 2025 01:05:39 +0100
Allow 2 in-process UIs
This allow MiddleWare to exchange OSC messages with a further, second
in-process UI. This will be needed for a further commit where
both PortChecker's clients will be in-place UIs.
Since this is only needed for PortChecker, the second UI is reduced to
OSC messaging and on purpose has no `Fl_Osc_Interface`.
Diffstat:
8 files changed, 105 insertions(+), 90 deletions(-)
diff --git a/src/Misc/MiddleWare.cpp b/src/Misc/MiddleWare.cpp
@@ -661,7 +661,8 @@ public:
//Give it to the backend and wait for the old part to return for
//deallocation
parent->transmitMsg("/load-part", "ib", npart, sizeof(Part *), &p);
- GUI::raiseUi(ui, "/damage", "s", ("/part" + to_s(npart) + "/").c_str());
+ for(void* uihandle : ui)
+ GUI::raiseUi(uihandle, "/damage", "s", ("/part" + to_s(npart) + "/").c_str());
}
//Well, you don't get much crazier than changing out all of your RT
@@ -972,9 +973,6 @@ public:
//Only valid until freed
Master *previous_master = nullptr;
- //The ONLY means that any chunk of UI code should have for interacting with the
- //backend
- Fl_Osc_Interface *osc;
//Synth Engine Parameters
ParamStore kits;
@@ -982,10 +980,15 @@ public:
void(*idle)(void*);
void* idle_ptr;
- //General UI callback
- cb_t cb;
- //UI handle
- void *ui;
+ //General UI callbacks
+ cb_t cb[2];
+ //UI handles
+ void *ui[2];
+
+ //The ONLY means that any chunk of UI code should have for interacting with the
+ //backend
+ //Note: Only the first UI is defined to have such an interface
+ Fl_Osc_Interface *osc;
std::atomic_int pending_load[NUM_MIDI_PARTS];
std::atomic_int actual_load[NUM_MIDI_PARTS];
@@ -1934,7 +1937,7 @@ static rtosc::Ports middlewareReplyPorts = {
MiddleWareImpl::MiddleWareImpl(MiddleWare *mw, SYNTH_T synth_,
Config* config, int preferrred_port)
- :parent(mw), config(config), ui(nullptr), synth(std::move(synth_)),
+ :parent(mw), config(config), ui{nullptr,nullptr}, synth(std::move(synth_)),
presetsstore(*config), autoSave(-1, [this]() {
auto master = this->master;
this->doReadOnlyOp([master](){
@@ -1962,7 +1965,8 @@ MiddleWareImpl::MiddleWareImpl(MiddleWare *mw, SYNTH_T synth_,
//dummy callback for starters
- cb = [](void*, const char*){};
+ for(cb_t& uicb : cb)
+ uicb = [](void*, const char*){};
idle = 0;
idle_ptr = 0;
@@ -2191,12 +2195,13 @@ bool MiddleWareImpl::doReadOnlyOpNormal(std::function<void()> read_only_fn, bool
void MiddleWareImpl::broadcastToRemote(const char *rtmsg)
{
- //Always send to the local UI
+ //Always send to the local UIs
sendToRemote(rtmsg, "GUI");
+ sendToRemote(rtmsg, "GUI2");
//Send to remote UI if there's one listening
for(auto rem:known_remotes)
- if(rem != "GUI")
+ if(rem != "GUI" && rem != "GUI2")
sendToRemote(rtmsg, rem);
broadcast = false;
@@ -2213,7 +2218,9 @@ void MiddleWareImpl::sendToRemote(const char *rtmsg, std::string dest)
//printf("sendToRemote(%s:%s,%s)\n", rtmsg, rtosc_argument_string(rtmsg),
// dest.c_str());
if(dest == "GUI") {
- cb(ui, rtmsg);
+ cb[0](ui[0], rtmsg);
+ } else if(dest == "GUI2") {
+ cb[1](ui[1], rtmsg);
} else if(!dest.empty()) {
lo_message msg = lo_message_deserialise((void*)rtmsg,
rtosc_message_length(rtmsg, bToU->buffer_size()), NULL);
@@ -2534,10 +2541,11 @@ void MiddleWare::doReadOnlyOp(std::function<void()> fn)
impl->doReadOnlyOp(fn);
}
-void MiddleWare::setUiCallback(void(*cb)(void*,const char *), void *ui)
+void MiddleWare::setUiCallback(int gui_id, void(*cb)(void*,const char *), void *ui)
{
- impl->cb = cb;
- impl->ui = ui;
+ assert(gui_id < sizeof(impl->cb)/sizeof(impl->cb[0]));
+ impl->cb[gui_id] = cb;
+ impl->ui[gui_id] = ui;
}
void MiddleWare::setIdleCallback(void(*cb)(void*), void *ptr)
@@ -2572,32 +2580,35 @@ void MiddleWare::transmitMsg_va(const char *path, const char *args, va_list va)
fprintf(stderr, "Error in transmitMsg(va)n");
}
-void MiddleWare::transmitMsgGui(const char *msg)
+void MiddleWare::transmitMsgGui(int gui_id, const char *msg)
{
- if(activeUrl() != "GUI") {
+ if(gui_id == 0 && activeUrl() != "GUI") {
transmitMsg("/echo", "ss", "OSC_URL", "GUI");
activeUrl("GUI");
+ } else if(gui_id == 1 && activeUrl() != "GUI2") {
+ transmitMsg("/echo", "ss", "OSC_URL", "GUI2");
+ activeUrl("GUI2");
}
transmitMsg(msg);
}
-void MiddleWare::transmitMsgGui(const char *path, const char *args, ...)
+void MiddleWare::transmitMsgGui(int gui_id, const char *path, const char *args, ...)
{
char buffer[1024];
va_list va;
va_start(va,args);
if(rtosc_vmessage(buffer,1024,path,args,va))
- transmitMsgGui(buffer);
+ transmitMsgGui(gui_id, buffer);
else
fprintf(stderr, "Error in transmitMsgGui(...)\n");
va_end(va);
}
-void MiddleWare::transmitMsgGui_va(const char *path, const char *args, va_list va)
+void MiddleWare::transmitMsgGui_va(int gui_id, const char *path, const char *args, va_list va)
{
char buffer[1024];
if(rtosc_vmessage(buffer, 1024, path, args, va))
- transmitMsgGui(buffer);
+ transmitMsgGui(gui_id, buffer);
else
fprintf(stderr, "Error in transmitMsgGui(va)n");
}
diff --git a/src/Misc/MiddleWare.h b/src/Misc/MiddleWare.h
@@ -51,7 +51,7 @@ class MiddleWare
//return UI interface
Fl_Osc_Interface *spawnUiApi(void);
//Set callback to push UI events to
- void setUiCallback(void(*cb)(void*,const char *),void *ui);
+ void setUiCallback(int gui_id, void(*cb)(void*,const char *),void *ui);
//Set callback to run while busy
void setIdleCallback(void(*cb)(void*),void *ptr);
//Handle events
@@ -66,11 +66,11 @@ class MiddleWare
void transmitMsg_va(const char *, const char *args, va_list va);
//Handle a rtosc Message uToB, if sender is GUI
- void transmitMsgGui(const char * msg);
+ void transmitMsgGui(int gui_id, const char * msg);
//Handle a rtosc Message uToB, if sender is GUI
- void transmitMsgGui(const char *, const char *args, ...);
+ void transmitMsgGui(int gui_id, const char *, const char *args, ...);
//Handle a rtosc Message uToB, if sender is GUI
- void transmitMsgGui_va(const char *, const char *args, va_list va);
+ void transmitMsgGui_va(int gui_id, const char *, const char *args, va_list va);
//Send a message to middleware from an arbitrary thread
void messageAnywhere(const char *msg, const char *args, ...);
diff --git a/src/Plugin/ZynAddSubFX/ZynAddSubFX.cpp b/src/Plugin/ZynAddSubFX/ZynAddSubFX.cpp
@@ -528,7 +528,7 @@ private:
void _initMaster()
{
middleware = new zyn::MiddleWare(std::move(synth), &config);
- middleware->setUiCallback(__uiCallback, this);
+ middleware->setUiCallback(0, __uiCallback, this);
middleware->setIdleCallback(__idleCallback, this);
_masterChangedCallback(middleware->spawnMaster());
diff --git a/src/Tests/PortChecker.cpp b/src/Tests/PortChecker.cpp
@@ -24,13 +24,10 @@ class PortChecker
void _masterChangedCallback(zyn::Master* m)
{
master = m;
- master->setMasterChangedCallback(__masterChangedCallback, this);
- }
-
- // TODO: eliminate static callbacks
- static void __masterChangedCallback(void* ptr, zyn::Master* m)
- {
- ((PortChecker*)ptr)->_masterChangedCallback(m);
+ master->setMasterChangedCallback(
+ [](void* p, zyn::Master* m) {
+ ((PortChecker*)p)->_masterChangedCallback(m); },
+ this);
}
void setUp() {
@@ -44,7 +41,14 @@ class PortChecker
synth->alias();
mw = new zyn::MiddleWare(std::move(*synth), &config);
- mw->setUiCallback(_uiCallback, this);
+ mw->setUiCallback(0,
+ [](void* p, const char* msg) {
+ ((PortChecker*)p)->uiCallback0(msg); },
+ this);
+ mw->setUiCallback(1,
+ [](void* p, const char* msg) {
+ ((PortChecker*)p)->uiCallback1(msg); },
+ this);
_masterChangedCallback(mw->spawnMaster());
realtime = nullptr;
}
@@ -57,7 +61,12 @@ class PortChecker
delete synth;
}
- void uiCallback(const char* msg)
+ void uiCallback0(const char* msg)
+ {
+ (void)msg;
+ }
+
+ void uiCallback1(const char* msg)
{
(void)msg;
}
@@ -76,11 +85,6 @@ class PortChecker
PortChecker() { setUp(); }
~PortChecker() { tearDown(); }
- static void _uiCallback(void* ptr, const char* msg)
- {
- ((PortChecker*)ptr)->uiCallback(msg);
- }
-
int run()
{
assert(mw);
diff --git a/src/Tests/SaveOSC.cpp b/src/Tests/SaveOSC.cpp
@@ -72,7 +72,7 @@ class SaveOSCTest
synth->alias();
mw = new zyn::MiddleWare(std::move(*synth), &config);
- mw->setUiCallback(_uiCallback, this);
+ mw->setUiCallback(0, _uiCallback, this);
_masterChangedCallback(mw->spawnMaster());
realtime = nullptr;
}
@@ -106,9 +106,9 @@ class SaveOSCTest
if(prependArg == -1)
{
- mw->transmitMsgGui(osc_path, "stT", arg1, start_time.val.t);
+ mw->transmitMsgGui(0, osc_path, "stT", arg1, start_time.val.t);
} else {
- mw->transmitMsgGui(osc_path, "istT", prependArg, arg1, start_time.val.t);
+ mw->transmitMsgGui(0, osc_path, "istT", prependArg, arg1, start_time.val.t);
}
int attempt;
@@ -236,7 +236,7 @@ class SaveOSCTest
if (strstr(filename.c_str(), ".xiz") ==
filename.c_str() + filename.length() - 4)
{
- mw->transmitMsgGui("/reset_master", "");
+ mw->transmitMsgGui(0, "/reset_master", "");
fprintf(stderr, "Loading XIZ file %s...\n", filename.c_str());
load_ok = timeOutOperation("/load_xiz", filename.c_str(), 1000, 0);
}
@@ -281,39 +281,39 @@ class SaveOSCTest
int test_presets()
{
// enable almost everything, in order to test all ports
- mw->transmitMsgGui("/part0/kit0/adpars/VoicePar0/PFilterEnabled", "T");
- mw->transmitMsgGui("/part0/kit0/adpars/VoicePar0/PFreqEnvelopeEnabled", "T");
- mw->transmitMsgGui("/part0/kit0/adpars/VoicePar0/PFreqLfoEnabled", "T");
- mw->transmitMsgGui("/part0/kit0/adpars/VoicePar0/PAAEnabled", "T");
- mw->transmitMsgGui("/part0/kit0/adpars/VoicePar0/PAmpEnvelopeEnabled", "T");
- mw->transmitMsgGui("/part0/kit0/adpars/VoicePar0/PAmpLfoEnabled", "T");
- mw->transmitMsgGui("/part0/kit0/adpars/VoicePar0/PFilterEnabled", "T");
- mw->transmitMsgGui("/part0/kit0/adpars/VoicePar0/PFilterEnvelopeEnabled", "T");
- mw->transmitMsgGui("/part0/kit0/adpars/VoicePar0/PFilterLfoEnabled", "T");
- mw->transmitMsgGui("/part0/kit0/adpars/VoicePar0/PFMEnabled", "i", 1);
- mw->transmitMsgGui("/part0/kit0/adpars/VoicePar0/PFMFreqEnvelopeEnabled", "T");
- mw->transmitMsgGui("/part0/kit0/adpars/VoicePar0/PFMAmpEnvelopeEnabled", "T");
- mw->transmitMsgGui("/part0/kit0/Psubenabled", "T");
- mw->transmitMsgGui("/part0/kit0/subpars/PBandWidthEnvelopeEnabled", "T");
- mw->transmitMsgGui("/part0/kit0/subpars/PFreqEnvelopeEnabled", "T");
- mw->transmitMsgGui("/part0/kit0/subpars/PGlobalFilterEnabled", "T");
- mw->transmitMsgGui("/part0/kit0/Ppadenabled", "T");
+ mw->transmitMsgGui(0, "/part0/kit0/adpars/VoicePar0/PFilterEnabled", "T");
+ mw->transmitMsgGui(0, "/part0/kit0/adpars/VoicePar0/PFreqEnvelopeEnabled", "T");
+ mw->transmitMsgGui(0, "/part0/kit0/adpars/VoicePar0/PFreqLfoEnabled", "T");
+ mw->transmitMsgGui(0, "/part0/kit0/adpars/VoicePar0/PAAEnabled", "T");
+ mw->transmitMsgGui(0, "/part0/kit0/adpars/VoicePar0/PAmpEnvelopeEnabled", "T");
+ mw->transmitMsgGui(0, "/part0/kit0/adpars/VoicePar0/PAmpLfoEnabled", "T");
+ mw->transmitMsgGui(0, "/part0/kit0/adpars/VoicePar0/PFilterEnabled", "T");
+ mw->transmitMsgGui(0, "/part0/kit0/adpars/VoicePar0/PFilterEnvelopeEnabled", "T");
+ mw->transmitMsgGui(0, "/part0/kit0/adpars/VoicePar0/PFilterLfoEnabled", "T");
+ mw->transmitMsgGui(0, "/part0/kit0/adpars/VoicePar0/PFMEnabled", "i", 1);
+ mw->transmitMsgGui(0, "/part0/kit0/adpars/VoicePar0/PFMFreqEnvelopeEnabled", "T");
+ mw->transmitMsgGui(0, "/part0/kit0/adpars/VoicePar0/PFMAmpEnvelopeEnabled", "T");
+ mw->transmitMsgGui(0, "/part0/kit0/Psubenabled", "T");
+ mw->transmitMsgGui(0, "/part0/kit0/subpars/PBandWidthEnvelopeEnabled", "T");
+ mw->transmitMsgGui(0, "/part0/kit0/subpars/PFreqEnvelopeEnabled", "T");
+ mw->transmitMsgGui(0, "/part0/kit0/subpars/PGlobalFilterEnabled", "T");
+ mw->transmitMsgGui(0, "/part0/kit0/Ppadenabled", "T");
// use all effects as ins fx
- mw->transmitMsgGui("/insefx0/efftype", "S", "Reverb");
- mw->transmitMsgGui("/insefx1/efftype", "S", "Phaser");
- mw->transmitMsgGui("/insefx2/efftype", "S", "Echo");
- mw->transmitMsgGui("/insefx3/efftype", "S", "Distortion");
- mw->transmitMsgGui("/insefx4/efftype", "S", "Sympathetic");
- mw->transmitMsgGui("/insefx5/efftype", "S", "DynFilter");
- mw->transmitMsgGui("/insefx6/efftype", "S", "Alienwah");
- mw->transmitMsgGui("/insefx7/efftype", "S", "EQ");
- mw->transmitMsgGui("/part0/partefx0/efftype", "S", "Chorus");
+ mw->transmitMsgGui(0, "/insefx0/efftype", "S", "Reverb");
+ mw->transmitMsgGui(0, "/insefx1/efftype", "S", "Phaser");
+ mw->transmitMsgGui(0, "/insefx2/efftype", "S", "Echo");
+ mw->transmitMsgGui(0, "/insefx3/efftype", "S", "Distortion");
+ mw->transmitMsgGui(0, "/insefx4/efftype", "S", "Sympathetic");
+ mw->transmitMsgGui(0, "/insefx5/efftype", "S", "DynFilter");
+ mw->transmitMsgGui(0, "/insefx6/efftype", "S", "Alienwah");
+ mw->transmitMsgGui(0, "/insefx7/efftype", "S", "EQ");
+ mw->transmitMsgGui(0, "/part0/partefx0/efftype", "S", "Chorus");
// use all effects as sys fx (except Chorus, it does not differ)
- mw->transmitMsgGui("/sysefx0/efftype", "S", "Reverb");
- mw->transmitMsgGui("/sysefx1/efftype", "S", "Phaser");
- mw->transmitMsgGui("/sysefx2/efftype", "S", "Echo");
- mw->transmitMsgGui("/sysefx3/efftype", "S", "Distortion");
+ mw->transmitMsgGui(0, "/sysefx0/efftype", "S", "Reverb");
+ mw->transmitMsgGui(0, "/sysefx1/efftype", "S", "Phaser");
+ mw->transmitMsgGui(0, "/sysefx2/efftype", "S", "Echo");
+ mw->transmitMsgGui(0, "/sysefx3/efftype", "S", "Distortion");
int res = EXIT_SUCCESS;
@@ -329,21 +329,21 @@ class SaveOSCTest
int npresets_part[] = {10};
for(; insefxstr[7] < '8'; ++insefxstr[7])
if(preset < npresets_ins[insefxstr[7]-'0'])
- mw->transmitMsgGui(insefxstr, "i", preset);
+ mw->transmitMsgGui(0, insefxstr, "i", preset);
for(; partefxstr[14] < '1'; ++partefxstr[14])
if(preset < npresets_part[partefxstr[14]-'0'])
- mw->transmitMsgGui(partefxstr, "i", preset);
+ mw->transmitMsgGui(0, partefxstr, "i", preset);
if(preset == 13) // for presets 13-17, test the up to 5 sysefx
{
- mw->transmitMsgGui("/sysefx0/efftype", "S", "Sympathetic");
- mw->transmitMsgGui("/sysefx1/efftype", "S", "DynFilter");
- mw->transmitMsgGui("/sysefx2/efftype", "S", "Alienwah");
- mw->transmitMsgGui("/sysefx3/efftype", "S", "EQ");
+ mw->transmitMsgGui(0, "/sysefx0/efftype", "S", "Sympathetic");
+ mw->transmitMsgGui(0, "/sysefx1/efftype", "S", "DynFilter");
+ mw->transmitMsgGui(0, "/sysefx2/efftype", "S", "Alienwah");
+ mw->transmitMsgGui(0, "/sysefx3/efftype", "S", "EQ");
}
int type_offset = (preset>=13)?4:0;
for(; sysefxstr[7] < '4'; ++sysefxstr[7])
if(preset%13 < npresets_ins[sysefxstr[7]-'0'+type_offset])
- mw->transmitMsgGui(sysefxstr, "i", preset%13);
+ mw->transmitMsgGui(0, sysefxstr, "i", preset%13);
char filename[] = "file0";
filename[4] += preset;
diff --git a/src/UI/Connection.cpp b/src/UI/Connection.cpp
@@ -295,7 +295,7 @@ class UI_Interface:public Fl_Osc_Interface
char *tmp = strdup(s.c_str());
s = rtosc::Ports::collapsePath(tmp);
free(tmp);
- impl->transmitMsgGui(s.c_str(),"");
+ impl->transmitMsgGui(0, s.c_str(),"");
}
void write(string s, const char *args, ...) override
@@ -309,7 +309,7 @@ class UI_Interface:public Fl_Osc_Interface
////fprintf(stderr, ".");
//fprintf(stderr, "write(%s:%s)\n", s.c_str(), args);
//fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
- impl->transmitMsgGui_va(s.c_str(), args, va);
+ impl->transmitMsgGui_va(0, s.c_str(), args, va);
va_end(va);
}
@@ -319,7 +319,7 @@ class UI_Interface:public Fl_Osc_Interface
////fprintf(stderr, ".");
//fprintf(stderr, "rawWrite(%s:%s)\n", msg, rtosc_argument_string(msg));
//fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
- impl->transmitMsgGui(rtosc::Ports::collapsePath((char*)msg));
+ impl->transmitMsgGui(0, rtosc::Ports::collapsePath((char*)msg));
}
void writeValue(string s, string ss) override
diff --git a/src/UI/NSM.C b/src/UI/NSM.C
@@ -62,7 +62,7 @@ NSM_Client::command_save(char **out_msg)
if(!project_filename)
return ERR_NO_SESSION_OPEN;
- middleware->transmitMsgGui("/save_xmz", "s", project_filename);
+ middleware->transmitMsgGui(0, "/save_xmz", "s", project_filename);
return r;
}
@@ -97,9 +97,9 @@ NSM_Client::command_open(const char *name,
int r = ERR_OK;
if(0 == stat(new_filename, &st))
- middleware->transmitMsgGui("/load_xmz", "s", new_filename);
+ middleware->transmitMsgGui(0, "/load_xmz", "s", new_filename);
else
- middleware->transmitMsgGui("/reset_master", "");
+ middleware->transmitMsgGui(0, "/reset_master", "");
if(project_filename)
free(project_filename);
diff --git a/src/main.cpp b/src/main.cpp
@@ -630,7 +630,7 @@ int main(int argc, char *argv[])
printf("[INFO] startup OSC\n");
typedef std::vector<const char *> wait_t;
wait_t msg_waitlist;
- middleware->setUiCallback([](void*v,const char*msg) {
+ middleware->setUiCallback(0, [](void*v,const char*msg) {
wait_t &wait = *(wait_t*)v;
size_t len = rtosc_message_length(msg, -1);
char *copy = new char[len];
@@ -641,7 +641,7 @@ int main(int argc, char *argv[])
printf("[INFO] UI calbacks\n");
if(!noui)
gui = GUI::createUi(middleware->spawnUiApi(), &Pexitprogram);
- middleware->setUiCallback(GUI::raiseUi, gui);
+ middleware->setUiCallback(0, GUI::raiseUi, gui);
middleware->setIdleCallback([](void*){GUI::tickUi(gui);}, NULL);
//Replay Startup Responses