commit a3a9423aed173013cd8cc6c0477e8e6048bc34ad
parent 037152b9a61d5abeeb91f2f9ba987afc5ba11ac9
Author: fundamental <[email protected]>
Date: Sun, 1 Nov 2015 20:08:14 -0500
Merge branch 'midi-serialization'
Conflicts:
src/Misc/MiddleWare.cpp
Diffstat:
22 files changed, 1202 insertions(+), 732 deletions(-)
diff --git a/src/Effects/EffectMgr.cpp b/src/Effects/EffectMgr.cpp
@@ -45,7 +45,7 @@ static const rtosc::Ports local_ports = {
rSelf(EffectMgr),
rPaste,
rRecurp(filterpars, "Filter Parameter for Dynamic Filter"),
- {"parameter#128::i:T:F", rProp(parameter) rProp(alias) rDoc("Parameter Accessor"),
+ {"parameter#128::i:T:F", rProp(parameter) rProp(alias) rLinear(0,127) rDoc("Parameter Accessor"),
NULL,
[](const char *msg, rtosc::RtData &d)
{
diff --git a/src/Misc/Master.cpp b/src/Misc/Master.cpp
@@ -166,6 +166,10 @@ static const Ports master_ports = {
[](const char *m,RtData &d){
Master *M = (Master*)d.obj;
M->noteOff(rtosc_argument(m,0).i,rtosc_argument(m,1).i);}},
+ {"virtual_midi_cc:iii", rDoc("MIDI CC Event"), 0,
+ [](const char *m,RtData &d){
+ Master *M = (Master*)d.obj;
+ M->setController(rtosc_argument(m,0).i,rtosc_argument(m,1).i,rtosc_argument(m,2).i);}},
{"setController:iii", rDoc("MIDI CC Event"), 0,
[](const char *m,RtData &d){
@@ -189,19 +193,13 @@ static const Ports master_ports = {
[](const char *,RtData &d) {
Master *M = (Master*)d.obj;
M->frozenState = false;}},
- {"register:iis", rDoc("MIDI Mapping Registration"), 0,
- [](const char *m,RtData &d){
- Master *M = (Master*)d.obj;
- M->midi.addElm(rtosc_argument(m,0).i, rtosc_argument(m,1).i,rtosc_argument(m,2).s);}},
- {"learn:s", rDoc("Begin Learning for specified address"), 0,
- [](const char *m, RtData &d){
- Master *M = (Master*)d.obj;
- printf("learning '%s'\n", rtosc_argument(m,0).s);
- M->midi.learn(rtosc_argument(m,0).s);}},
- {"unlearn:s", rDoc("Remove Learning for specified address"), 0,
- [](const char *m, RtData &d){
+ {"midi-learn/", 0, &rtosc::MidiMapperRT::ports,
+ [](const char *msg, RtData &d) {
Master *M = (Master*)d.obj;
- M->midi.clear_entry(rtosc_argument(m,0).s);}},
+ SNIP;
+ printf("residue message = <%s>\n", msg);
+ d.obj = &M->midi;
+ rtosc::MidiMapperRT::ports.dispatch(msg,d);}},
{"close-ui:", rDoc("Request to close any connection named \"GUI\""), 0,
[](const char *, RtData &d) {
d.reply("/close-ui", "");}},
@@ -228,7 +226,7 @@ static const Ports master_ports = {
{"undo_resume:",rProp(internal) rDoc("resume undo event recording"),0,
[](const char *, rtosc::RtData &d) {d.reply("/undo_resume", "");}},
{"config/", rDoc("Top Level Application Configuration Parameters"), &Config::ports,
- [](const char *, rtosc::RtData &){}},
+ [](const char *, rtosc::RtData &d){d.forward();}},
{"presets/", rDoc("Parameter Presets"), &preset_ports, rBOIL_BEGIN
SNIP
preset_ports.dispatch(msg, data);
@@ -236,12 +234,6 @@ static const Ports master_ports = {
};
const Ports &Master::ports = master_ports;
-#ifndef PLUGINVERSION
-//XXX HACKS
-Master *the_master;
-rtosc::ThreadLink *the_bToU;
-#endif
-
class DataObj:public rtosc::RtData
{
public:
@@ -252,6 +244,7 @@ class DataObj:public rtosc::RtData
loc_size = loc_size_;
obj = obj_;
bToU = bToU_;
+ forwarded = false;
}
virtual void reply(const char *path, const char *args, ...) override
@@ -280,9 +273,18 @@ class DataObj:public rtosc::RtData
}
virtual void broadcast(const char *msg) override
{
- reply("/broadcast");
+ reply("/broadcast", "");
reply(msg);
- };
+ }
+
+ virtual void forward(const char *reason) override
+ {
+ assert(message);
+ reply("/forward", "");
+ printf("forwarding '%s'\n", message);
+ forwarded = true;
+ }
+ bool forwarded;
private:
rtosc::ThreadLink *bToU;
};
@@ -295,20 +297,22 @@ vuData::vuData(void)
Master::Master(const SYNTH_T &synth_, Config* config)
:HDDRecorder(synth_), ctl(synth_),
microtonal(config->cfg.GzipCompression), bank(config),
- midi(Master::ports), frozenState(false), pendingMemory(false),
+ frozenState(false), pendingMemory(false),
synth(synth_), time(synth), gzip_compression(config->cfg.GzipCompression)
{
bToU = NULL;
uToB = NULL;
+
+ //Setup MIDI
+ midi.frontend = [this](const char *msg) {bToU->raw_write(msg);};
+ midi.backend = [this](const char *msg) {applyOscEvent(msg);};
+
memory = new AllocatorClass();
swaplr = 0;
off = 0;
smps = 0;
bufl = new float[synth.buffersize];
bufr = new float[synth.buffersize];
-#ifndef PLUGINVERSION
- the_master = this;
-#endif
last_xmz[0] = 0;
fft = new FFTwrapper(synth.oscilsize);
@@ -334,24 +338,6 @@ Master::Master(const SYNTH_T &synth_, Config* config)
defaults();
-#ifndef PLUGINVERSION
- midi.event_cb = [](const char *m)
- {
- char loc_buf[1024];
- DataObj d{loc_buf, 1024, the_master, the_bToU};
- memset(loc_buf, 0, sizeof(loc_buf));
- //printf("sending an event to the owner of '%s'\n", m);
- Master::ports.dispatch(m+1, d);
- };
-#else
- midi.event_cb = [](const char *) {};
-#endif
-
- midi.error_cb = [](const char *a, const char *b)
- {
- fprintf(stderr, "MIDI- got an error '%s' -- '%s'\n",a,b);
- };
-
mastercb = 0;
mastercb_ptr = 0;
}
@@ -362,9 +348,19 @@ void Master::applyOscEvent(const char *msg)
DataObj d{loc_buf, 1024, this, bToU};
memset(loc_buf, 0, sizeof(loc_buf));
d.matches = 0;
- ports.dispatch(msg+1, d);
- if(d.matches == 0)
- fprintf(stderr, "Unknown path '%s'\n", msg);
+
+ if(strcmp(msg, "/get-vu") && false) {
+ fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 5 + 30, 0 + 40);
+ fprintf(stdout, "backend[*]: '%s'<%s>\n", msg,
+ rtosc_argument_string(msg));
+ fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
+ }
+
+ ports.dispatch(msg, d, true);
+ if(d.matches == 0 && !d.forwarded)
+ fprintf(stderr, "Unknown path '%s:%s'\n", msg, rtosc_argument_string(msg));
+ if(d.forwarded)
+ bToU->raw_write(msg);
}
void Master::defaults()
@@ -450,7 +446,8 @@ void Master::setController(char chan, int type, int par)
{
if(frozenState)
return;
- midi.process(chan,type,par);
+ //TODO add chan back
+ midi.handleCC(type,par);
if((type == C_dataentryhi) || (type == C_dataentrylo)
|| (type == C_nrpnhi) || (type == C_nrpnlo)) { //Process RPN and NRPN by the Master (ignore the chan)
ctl.setparameternumber(type, par);
@@ -658,9 +655,7 @@ void Master::AudioOut(float *outl, float *outr)
rtosc_argument_string(msg));
fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
}
- d.matches = 0;
- //fprintf(stdout, "address '%s'\n", uToB->peak());
- ports.dispatch(msg+1, d);
+ ports.dispatch(msg, d, true);
events++;
if(!d.matches) {// && !ports.apropos(msg)) {
fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 1, 7 + 30, 0 + 40);
diff --git a/src/Misc/Master.h b/src/Misc/Master.h
@@ -166,7 +166,7 @@ class Master
//Statistics on output levels
vuData vu;
- rtosc::MidiTable midi;//<1024,64>
+ rtosc::MidiMapperRT midi;
bool frozenState;//read-only parameters for threadsafe actions
Allocator *memory;
diff --git a/src/Misc/MiddleWare.cpp b/src/Misc/MiddleWare.cpp
@@ -40,9 +40,6 @@
#endif
using std::string;
-#ifndef PLUGINVERSION
-extern rtosc::ThreadLink *the_bToU;//XXX
-#endif
/******************************************************************************
* LIBLO And Reflection Code *
@@ -188,131 +185,54 @@ void preparePadSynth(string path, PADnoteParameters *p, rtosc::ThreadLink *uToB)
}
}
-/*****************************************************************************
- * Instrument Banks *
- * *
- * Banks and presets in general are not classed as realtime safe *
- * *
- * The supported operations are: *
- * - Load Names *
- * - Load Bank *
- * - Refresh List of Banks *
- *****************************************************************************/
-void refreshBankView(const Bank &bank, unsigned loc, std::function<void(const char*)> cb)
-{
- if(loc >= BANK_SIZE)
- return;
-
- char response[2048];
- if(!rtosc_message(response, 1024, "/bankview", "iss",
- loc, bank.ins[loc].name.c_str(),
- bank.ins[loc].filename.c_str()))
- errx(1, "Failure to handle bank update properly...");
-
-
- if(cb)
- cb(response);
-}
-
-void bankList(Bank &bank, std::function<void(const char*)> cb)
-{
- char response[2048];
- int i = 0;
-
- for(auto &elm : bank.banks) {
- if(!rtosc_message(response, 2048, "/bank-list", "iss",
- i++, elm.name.c_str(), elm.dir.c_str()))
- errx(1, "Failure to handle bank update properly...");
- if(cb)
- cb(response);
- }
-}
-
-void rescanForBanks(Bank &bank, std::function<void(const char*)> cb)
-{
- bank.rescanforbanks();
- bankList(bank, cb);
-}
-
-void loadBank(Bank &bank, int pos, std::function<void(const char*)> cb)
+/******************************************************************************
+ * MIDI Serialization *
+ * *
+ ******************************************************************************/
+void saveMidiLearn(XMLwrapper &xml, const rtosc::MidiMappernRT &midi)
{
- char response[2048];
- if(!rtosc_message(response, 2048, "/loadbank", "i", pos))
- errx(1, "Failure to handle bank update properly...");
- if(cb)
- cb(response);
- if(bank.bankpos != pos) {
- bank.bankpos = pos;
- bank.loadbank(bank.banks[pos].dir);
- for(int i=0; i<BANK_SIZE; ++i)
- refreshBankView(bank, i, cb);
+ xml.beginbranch("midi-learn");
+ for(auto value:midi.inv_map) {
+ XmlNode binding("midi-binding");
+ auto biject = std::get<3>(value.second);
+ binding["osc-path"] = value.first;
+ binding["coarse-CC"] = to_s(std::get<1>(value.second));
+ binding["fine-CC"] = to_s(std::get<2>(value.second));
+ binding["type"] = "i";
+ binding["minimum"] = to_s(biject.min);
+ binding["maximum"] = to_s(biject.max);
+ xml.add(binding);
}
+ xml.endbranch();
}
-void bankPos(Bank &bank, std::function<void(const char *)> cb)
+void loadMidiLearn(XMLwrapper &xml, rtosc::MidiMappernRT &midi)
{
- char response[2048];
-
- if(!rtosc_message(response, 2048, "/loadbank", "i", bank.bankpos))
- errx(1, "Failure to handle bank update properly...");
- if(cb)
- cb(response);
-}
-
-/*****************************************************************************
- * Data Object for Non-RT Class Dispatch *
- *****************************************************************************/
+ using rtosc::Port;
+ if(xml.enterbranch("midi-learn")) {
+ auto nodes = xml.getBranch();
-class DummyDataObj:public rtosc::RtData
-{
- public:
- DummyDataObj(char *loc_, size_t loc_size_, void *obj_, MiddleWareImpl *mwi_,
- rtosc::ThreadLink *uToB_)
- {
- memset(loc_, 0, sizeof(loc_size_));
- buffer = new char[4*4096];
- memset(buffer, 0, 4*4096);
- loc = loc_;
- loc_size = loc_size_;
- obj = obj_;
- mwi = mwi_;
- uToB = uToB_;
- }
- ~DummyDataObj(void)
- {
- delete[] buffer;
- }
+ //TODO clear mapper
- virtual void reply(const char *path, const char *args, ...)
- {
- //printf("reply building '%s'\n", path);
- va_list va;
- va_start(va,args);
- if(!strcmp(path, "/forward")) { //forward the information to the backend
- args++;
- path = va_arg(va, const char *);
- //fprintf(stderr, "forwarding information to the backend on '%s'<%s>\n",
- // path, args);
- rtosc_vmessage(buffer,4*4096,path,args,va);
- uToB->raw_write(buffer);
+ for(auto node:nodes) {
+ if(node.name != "midi-binding" ||
+ !node.has("osc-path") ||
+ !node.has("coarse-CC"))
+ continue;
+ const string path = node["osc-path"];
+ const int CC = atoi(node["coarse-CC"].c_str());
+ const Port *p = Master::ports.apropos(path.c_str());
+ if(p) {
+ printf("loading midi port...\n");
+ midi.addNewMapper(CC, *p, path);
} else {
- //printf("path = '%s' args = '%s'\n", path, args);
- //printf("buffer = '%p'\n", buffer);
- rtosc_vmessage(buffer,4*4096,path,args,va);
- //printf("buffer = '%s'\n", buffer);
- reply(buffer);
+ printf("unknown midi bindable <%s>\n", path.c_str());
}
- va_end(va);
}
- virtual void reply(const char *msg);
- //virtual void broadcast(const char *path, const char *args, ...){(void)path;(void)args;};
- //virtual void broadcast(const char *msg){(void)msg;};
- private:
- char *buffer;
- MiddleWareImpl *mwi;
- rtosc::ThreadLink *uToB;
-};
-
+ xml.exitbranch();
+ } else
+ printf("cannot find 'midi-learn' branch...\n");
+}
/******************************************************************************
* Non-RealTime Object Store *
@@ -392,6 +312,23 @@ struct NonRtObjStore
{
return objmap[loc];
}
+
+ void handleOscil(const char *msg, rtosc::RtData &d) {
+ string obj_rl(d.message, msg);
+ void *osc = get(obj_rl);
+ assert(osc);
+ strcpy(d.loc, obj_rl.c_str());
+ d.obj = osc;
+ OscilGen::non_realtime_ports.dispatch(msg, d);
+ }
+ void handlePad(const char *msg, rtosc::RtData &d) {
+ string obj_rl(d.message, msg);
+ void *pad = get(obj_rl);
+ assert(pad);
+ strcpy(d.loc, obj_rl.c_str());
+ d.obj = pad;
+ PADnoteParameters::non_realtime_ports.dispatch(msg, d);
+ }
};
/******************************************************************************
@@ -463,10 +400,13 @@ namespace Nio
};
}
+
/* Implementation */
class MiddleWareImpl
{
+ public:
MiddleWare *parent;
+ private:
//Detect if the name of the process is 'zynaddsubfx'
bool isPlugin() const
@@ -481,76 +421,15 @@ class MiddleWareImpl
return true;
}
- Config* const config;
-
public:
+ Config* const config;
MiddleWareImpl(MiddleWare *mw, SYNTH_T synth, Config* config,
int preferred_port);
~MiddleWareImpl(void);
- void warnMemoryLeaks(void);
-
//Apply function while parameters are write locked
void doReadOnlyOp(std::function<void()> read_only_fn);
-
- void saveBankSlot(int npart, int nslot, Master *master)
- {
- int err = 0;
- doReadOnlyOp([master,nslot,npart,&err](){
- err = master->bank.savetoslot(nslot, master->part[npart]);});
- if(err) {
- char buffer[1024];
- rtosc_message(buffer, 1024, "/alert", "s",
- "Failed To Save To Bank Slot, please check file permissions");
- GUI::raiseUi(ui, buffer);
- }
- }
-
- void renameBankSlot(int slot, string name, Master *master)
- {
- int err = master->bank.setname(slot, name, -1);
- if(err) {
- char buffer[1024];
- rtosc_message(buffer, 1024, "/alert", "s",
- "Failed To Rename Bank Slot, please check file permissions");
- GUI::raiseUi(ui, buffer);
- }
- }
-
- void swapBankSlot(int slota, int slotb, Master *master)
- {
- int err = master->bank.swapslot(slota, slotb);
- if(err) {
- char buffer[1024];
- rtosc_message(buffer, 1024, "/alert", "s",
- "Failed To Swap Bank Slots, please check file permissions");
- GUI::raiseUi(ui, buffer);
- }
- }
-
- void clearBankSlot(int slot, Master *master)
- {
- int err = master->bank.clearslot(slot);
- if(err) {
- char buffer[1024];
- rtosc_message(buffer, 1024, "/alert", "s",
- "Failed To Clear Bank Slot, please check file permissions");
- GUI::raiseUi(ui, buffer);
- }
- }
-
- void saveMaster(const char *filename)
- {
- //Copy is needed as filename WILL get trashed during the rest of the run
- std::string fname = filename;
- //printf("saving master('%s')\n", filename);
- doReadOnlyOp([this,fname](){
- int res = master->saveXML(fname.c_str());
- (void)res;
- /*printf("results: '%s' '%d'\n",fname.c_str(), res);*/});
- }
-
void savePart(int npart, const char *filename)
{
//Copy is needed as filename WILL get trashed during the rest of the run
@@ -624,7 +503,7 @@ public:
//Give it to the backend and wait for the old part to return for
//deallocation
- uToB->write("/load-part", "ib", npart, sizeof(Part*), &p);
+ parent->transmitMsg("/load-part", "ib", npart, sizeof(Part*), &p);
GUI::raiseUi(ui, "/damage", "s", ("/part"+to_s(npart)+"/").c_str());
}
@@ -644,10 +523,8 @@ public:
//Give it to the backend and wait for the old part to return for
//deallocation
- uToB->write("/load-part", "ib", npart, sizeof(Part*), &p);
+ parent->transmitMsg("/load-part", "ib", npart, sizeof(Part*), &p);
GUI::raiseUi(ui, "/damage", "s", ("/part"+to_s(npart)+"/").c_str());
- //if(osc)
- // osc->damage(("/part"+to_s(npart)+"/").c_str());
}
//Well, you don't get much crazier than changing out all of your RT
@@ -672,7 +549,7 @@ public:
//Give it to the backend and wait for the old part to return for
//deallocation
- uToB->write("/load-master", "b", sizeof(Master*), &m);
+ parent->transmitMsg("/load-master", "b", sizeof(Master*), &m);
}
void updateResources(Master *m)
@@ -685,9 +562,13 @@ public:
//If currently broadcasting messages
bool broadcast = false;
+ //If message should be forwarded through snoop ports
+ bool forward = false;
+ //if message is in order or out-of-order execution
+ bool in_order = false;
//If accepting undo events as user driven
bool recording_undo = true;
- void bToUhandle(const char *rtmsg, bool dummy=false);
+ void bToUhandle(const char *rtmsg);
void tick(void)
{
@@ -699,86 +580,6 @@ public:
}
}
- bool handlePAD(string path, const char *msg, void *v)
- {
- if(!v)
- return true;
- char buffer[1024];
- memset(buffer, 0, sizeof(buffer));
- DummyDataObj d(buffer, 1024, v, this, uToB);
- strcpy(buffer, path.c_str());
-
- PADnoteParameters::ports.dispatch(msg, d);
- if(!d.matches) {
- fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 1, 7 + 30, 0 + 40);
- fprintf(stderr, "Unknown location '%s%s'<%s>\n",
- path.c_str(), msg, rtosc_argument_string(msg));
- fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
- }
-
- return true;
- }
-
- void handlePresets(const char *msg)
- {
- char buffer[1024];
- memset(buffer, 0, sizeof(buffer));
- DummyDataObj d(buffer, 1024, (void*)parent, this, uToB);
- strcpy(buffer, "/presets/");
-
- //012345678
- ///presets/
- real_preset_ports.dispatch(msg+9, d);
- //printf("Something <%s>\n", msg+9);
- if(strstr(msg, "paste") && rtosc_argument_string(msg)[0] == 's') {
- char buffer[1024];
- rtosc_message(buffer, 1024, "/damage", "s",
- rtosc_argument(msg, 0).s);
- GUI::raiseUi(ui, buffer);
- }
-
-
- if(!d.matches) {
- fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 1, 7 + 30, 0 + 40);
- fprintf(stderr, "Unknown location '%s'<%s>\n",
- msg, rtosc_argument_string(msg));
- fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
- }
- }
-
- void handleIo(const char *msg)
- {
- char buffer[1024];
- memset(buffer, 0, sizeof(buffer));
- DummyDataObj d(buffer, 1024, (void*)&config, this, uToB);
- strcpy(buffer, "/io/");
-
- Nio::ports.dispatch(msg+4, d);
- if(!d.matches) {
- fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 1, 7 + 30, 0 + 40);
- fprintf(stderr, "Unknown location '%s'<%s>\n",
- msg, rtosc_argument_string(msg));
- fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
- }
- }
-
- void handleConfig(const char *msg)
- {
- char buffer[1024];
- memset(buffer, 0, sizeof(buffer));
- DummyDataObj d(buffer, 1024, (void*)config, this, uToB);
- strcpy(buffer, "/config/");
-
- Config::ports.dispatch(msg+8, d);
- if(!d.matches) {
- fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 1, 7 + 30, 0 + 40);
- fprintf(stderr, "Unknown location '%s'<%s>\n",
- msg, rtosc_argument_string(msg));
- fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
- }
- }
-
- bool handleOscil(string path, const char *msg, void *v);
void kitEnable(const char *msg);
void kitEnable(int part, int kit, int type);
@@ -786,11 +587,21 @@ public:
// Handle an event with special cases
void handleMsg(const char *msg);
-
void write(const char *path, const char *args, ...);
void write(const char *path, const char *args, va_list va);
+ // Send a message to a remote client
+ void sendToRemote(const char *msg, std::string dest);
+ // Send a message to the current remote client
+ void sendToCurrentRemote(const char *msg)
+ {
+ sendToRemote(msg, in_order ? curr_url : last_url);
+ }
+ // Broadcast a message to all listening remote clients
+ void broadcastToRemote(const char *msg);
+
+
/*
* Provides a mapping for non-RT objects stored inside the backend
* - Oscilgen almost all parameters can be safely set
@@ -821,8 +632,12 @@ public:
std::atomic_int pending_load[NUM_MIDI_PARTS];
std::atomic_int actual_load[NUM_MIDI_PARTS];
+ //Undo/Redo
rtosc::UndoHistory undo;
+ //MIDI Learn
+ rtosc::MidiMappernRT midi_mapper;
+
//Link To the Realtime
rtosc::ThreadLink *bToU;
rtosc::ThreadLink *uToB;
@@ -833,10 +648,398 @@ public:
//Synthesis Rate Parameters
const SYNTH_T synth;
-
+
PresetsStore presetsstore;
};
+/*****************************************************************************
+ * Data Object for Non-RT Class Dispatch *
+ *****************************************************************************/
+
+class MwDataObj:public rtosc::RtData
+{
+ public:
+ MwDataObj(MiddleWareImpl *mwi_)
+ {
+ loc_size = 1024;
+ loc = new char[loc_size];
+ memset(loc, 0, loc_size);
+ buffer = new char[4*4096];
+ memset(buffer, 0, 4*4096);
+ obj = mwi_;
+ mwi = mwi_;
+ forwarded = false;
+ }
+
+ ~MwDataObj(void)
+ {
+ delete[] buffer;
+ }
+
+ //Replies and broadcasts go to the remote
+
+ //Chain calls repeat the call into handle()
+
+ //Forward calls send the message directly to the realtime
+ virtual void reply(const char *path, const char *args, ...)
+ {
+ //printf("reply building '%s'\n", path);
+ va_list va;
+ va_start(va,args);
+ if(!strcmp(path, "/forward")) { //forward the information to the backend
+ args++;
+ path = va_arg(va, const char *);
+ rtosc_vmessage(buffer,4*4096,path,args,va);
+ } else {
+ rtosc_vmessage(buffer,4*4096,path,args,va);
+ reply(buffer);
+ }
+ va_end(va);
+ }
+ virtual void reply(const char *msg){
+ mwi->sendToCurrentRemote(msg);
+ };
+ //virtual void broadcast(const char *path, const char *args, ...){(void)path;(void)args;};
+ //virtual void broadcast(const char *msg){(void)msg;};
+
+ virtual void chain(const char *msg) override
+ {
+ assert(msg);
+ printf("chain call on <%s>\n", msg);
+ mwi->handleMsg(msg);
+ }
+
+ virtual void chain(const char *path, const char *args, ...) override
+ {
+ assert(path);
+ va_list va;
+ va_start(va,args);
+ rtosc_vmessage(buffer,4*4096,path,args,va);
+ chain(buffer);
+ va_end(va);
+ }
+
+ virtual void forward(const char *) override
+ {
+ forwarded = true;
+ }
+
+ bool forwarded;
+ private:
+ char *buffer;
+ MiddleWareImpl *mwi;
+};
+
+
+
+
+static int extractInt(const char *msg)
+{
+ const char *mm = msg;
+ while(*mm && !isdigit(*mm)) ++mm;
+ if(isdigit(*mm))
+ return atoi(mm);
+ return -1;
+}
+
+static const char *chomp(const char *msg)
+{
+ while(*msg && *msg!='/') ++msg; \
+ msg = *msg ? msg+1 : msg;
+ return msg;
+};
+
+using rtosc::RtData;
+#define rObject Bank
+#define rBegin [](const char *msg, RtData &d) { (void)msg;(void)d;\
+ rObject &impl = *((rObject*)d.obj);(void)impl;
+#define rEnd }
+/*****************************************************************************
+ * Instrument Banks *
+ * *
+ * Banks and presets in general are not classed as realtime safe *
+ * *
+ * The supported operations are: *
+ * - Load Names *
+ * - Load Bank *
+ * - Refresh List of Banks *
+ *****************************************************************************/
+rtosc::Ports bankPorts = {
+ {"rescan:", 0, 0,
+ rBegin;
+ impl.rescanforbanks();
+ //Send updated banks
+ int i = 0;
+ for(auto &elm : impl.banks)
+ d.reply("/bank/bank_select", "iss", i++, elm.name.c_str(), elm.dir.c_str());
+
+ rEnd},
+ {"slot#1024:", 0, 0,
+ rBegin;
+ const int loc = extractInt(msg);
+ if(loc >= BANK_SIZE)
+ return;
+
+ d.reply("/bankview", "iss",
+ loc, impl.ins[loc].name.c_str(),
+ impl.ins[loc].filename.c_str());
+ rEnd},
+ {"banks:", 0, 0,
+ rBegin;
+ int i = 0;
+ for(auto &elm : impl.banks)
+ d.reply("/bank/bank_select", "iss", i++, elm.name.c_str(), elm.dir.c_str());
+ rEnd},
+ {"bank_select::i", 0, 0,
+ rBegin
+ if(rtosc_narguments(msg)) {
+ const int pos = rtosc_argument(msg, 0).i;
+ d.reply(d.loc, "i", pos);
+ if(impl.bankpos != pos) {
+ impl.bankpos = pos;
+ impl.loadbank(impl.banks[pos].dir);
+
+ //Reload bank slots
+ for(int i=0; i<BANK_SIZE; ++i)
+ d.reply("/bankview", "iss",
+ i, impl.ins[i].name.c_str(),
+ impl.ins[i].filename.c_str());
+ }
+ } else
+ d.reply("/bank/bank_select", "i", impl.bankpos);
+ rEnd},
+ {"rename_slot:is", 0, 0,
+ rBegin;
+ const int slot = rtosc_argument(msg, 0).i;
+ const char *name = rtosc_argument(msg, 1).s;
+ const int err = impl.setname(slot, name, -1);
+ if(err) {
+ d.reply("/alert", "s",
+ "Failed To Rename Bank Slot, please check file permissions");
+ }
+ rEnd},
+ {"swap_slots:ii", 0, 0,
+ rBegin;
+ const int slota = rtosc_argument(msg, 0).i;
+ const int slotb = rtosc_argument(msg, 1).i;
+ const int err = impl.swapslot(slota, slotb);
+ if(err)
+ d.reply("/alert", "s",
+ "Failed To Swap Bank Slots, please check file permissions");
+ rEnd},
+ {"clear_slot:i", 0, 0,
+ rBegin;
+ const int slot = rtosc_argument(msg, 0).i;
+ const int err = impl.clearslot(slot);
+ if(err)
+ d.reply("/alert", "s",
+ "Failed To Clear Bank Slot, please check file permissions");
+ rEnd},
+};
+
+/******************************************************************************
+ * MiddleWare Snooping Ports *
+ * *
+ * These ports handle: *
+ * - Events going to the realtime thread which cannot be safely handled *
+ * there *
+ * - Events generated by the realtime thread which are not destined for a *
+ * user interface *
+ ******************************************************************************/
+
+#undef rObject
+#define rObject MiddleWareImpl
+/*
+ * BASE/part#/kititem#
+ * BASE/part#/kit#/adpars/voice#/oscil/\*
+ * BASE/part#/kit#/adpars/voice#/mod-oscil/\*
+ * BASE/part#/kit#/padpars/prepare
+ * BASE/part#/kit#/padpars/oscil/\*
+ */
+static rtosc::Ports middwareSnoopPorts = {
+ {"part#16/kit#8/adpars/VoicePar#8/OscilSmp/", 0, &OscilGen::non_realtime_ports,
+ rBegin;
+ impl.obj_store.handleOscil(chomp(chomp(chomp(chomp(chomp(msg))))), d);
+ rEnd},
+ {"part#16/kit#8/adpars/VoicePar#8/FMSmp/", 0, &OscilGen::non_realtime_ports,
+ rBegin
+ impl.obj_store.handleOscil(chomp(chomp(chomp(chomp(chomp(msg))))), d);
+ rEnd},
+ {"part#16/kit#8/padpars/", 0, &PADnoteParameters::non_realtime_ports,
+ rBegin
+ impl.obj_store.handlePad(chomp(chomp(chomp(msg))), d);
+ rEnd},
+ {"bank/", 0, &bankPorts,
+ rBegin;
+ d.obj = &impl.master->bank;
+ bankPorts.dispatch(chomp(msg),d);
+ rEnd},
+ {"bank/save_to_slot:ii", 0, 0,
+ rBegin;
+ const int part_id = rtosc_argument(msg, 0).i;
+ const int slot = rtosc_argument(msg, 1).i;
+
+ int err = 0;
+ impl.doReadOnlyOp([&impl,slot,part_id,&err](){
+ err = impl.master->bank.savetoslot(slot, impl.master->part[part_id]);});
+ if(err) {
+ char buffer[1024];
+ rtosc_message(buffer, 1024, "/alert", "s",
+ "Failed To Save To Bank Slot, please check file permissions");
+ GUI::raiseUi(impl.ui, buffer);
+ }
+ rEnd},
+ {"config/", 0, &Config::ports,
+ rBegin;
+ d.obj = impl.config;
+ Config::ports.dispatch(chomp(msg), d);
+ rEnd},
+ {"presets/", 0, &real_preset_ports, [](const char *msg, RtData &d) {
+ MiddleWareImpl *obj = (MiddleWareImpl*)d.obj;
+ d.obj = (void*)obj->parent;
+ real_preset_ports.dispatch(chomp(msg), d);
+ if(strstr(msg, "paste") && rtosc_argument_string(msg)[0] == 's')
+ d.reply("/damage", "s", rtosc_argument(msg, 0).s);
+ }},
+ {"io/", 0, &Nio::ports, [](const char *msg, RtData &d) {
+ Nio::ports.dispatch(chomp(msg), d);}},
+ {"part*/kit*/{Padenabled,Ppadenabled,Psubenabled}:T:F", 0, 0,
+ rBegin;
+ impl.kitEnable(msg);
+ d.forward();
+ rEnd},
+ {"save_xlz:s", 0, 0,
+ rBegin;
+ const char *file = rtosc_argument(msg, 0).s;
+ XMLwrapper xml;
+ saveMidiLearn(xml, impl.midi_mapper);
+ xml.saveXMLfile(file, impl.master->gzip_compression);
+ rEnd},
+ {"load_xlz:s", 0, 0,
+ rBegin;
+ const char *file = rtosc_argument(msg, 0).s;
+ XMLwrapper xml;
+ xml.loadXMLfile(file);
+ loadMidiLearn(xml, impl.midi_mapper);
+ rEnd},
+ {"save_xmz:s", 0, 0,
+ rBegin;
+ const char *file = rtosc_argument(msg, 0).s;
+ //Copy is needed as filename WILL get trashed during the rest of the run
+ impl.doReadOnlyOp([&impl,file](){
+ int res = impl.master->saveXML(file);
+ (void)res;});
+ rEnd},
+ {"save_xiz:is", 0, 0,
+ rBegin;
+ const int part_id = rtosc_argument(msg,0).i;
+ const char *file = rtosc_argument(msg,1).s;
+ impl.savePart(part_id, file);
+ rEnd},
+ {"load_xmz:s", 0, 0,
+ rBegin;
+ const char *file = rtosc_argument(msg, 0).s;
+ impl.loadMaster(file);
+ rEnd},
+ {"reset_master:", 0, 0,
+ rBegin;
+ impl.loadMaster(NULL);
+ rEnd},
+ {"load_xiz:is", 0, 0,
+ rBegin;
+ const int part_id = rtosc_argument(msg,0).i;
+ const char *file = rtosc_argument(msg,1).s;
+ impl.pending_load[part_id]++;
+ impl.loadPart(part_id, file, impl.master);
+ rEnd},
+ {"load-part:is", 0, 0,
+ rBegin;
+ const int part_id = rtosc_argument(msg,0).i;
+ const char *file = rtosc_argument(msg,1).s;
+ impl.pending_load[part_id]++;
+ impl.loadPart(part_id, file, impl.master);
+ rEnd},
+ {"setprogram:i:c", 0, 0,
+ rBegin;
+ const int slot = rtosc_argument(msg, 0).i;
+ impl.pending_load[0]++;
+ impl.loadPart(0, impl.master->bank.ins[slot].filename.c_str(), impl.master);
+ rEnd},
+ {"part#16/clear:", 0, 0,
+ rBegin;
+ impl.loadClearPart(extractInt(msg));
+ rEnd},
+ {"undo:", 0, 0,
+ rBegin;
+ impl.undo.seekHistory(-1);
+ rEnd},
+ {"redo:", 0, 0,
+ rBegin;
+ impl.undo.seekHistory(+1);
+ rEnd},
+ {"learn:s", 0, 0,
+ rBegin;
+ string addr = rtosc_argument(msg, 0).s;
+ auto &midi = impl.midi_mapper;
+ auto map = midi.getMidiMappingStrings();
+ if(map.find(addr) != map.end())
+ midi.map(addr.c_str(), false);
+ else
+ midi.map(addr.c_str(), true);
+ rEnd},
+ //drop this message into the abyss
+ {"ui/title:", 0, 0, [](const char *msg, RtData &d) {}}
+};
+
+static rtosc::Ports middlewareReplyPorts = {
+ {"echo:ss", 0, 0,
+ rBegin;
+ const char *type = rtosc_argument(msg, 0).s;
+ const char *url = rtosc_argument(msg, 1).s;
+ if(!strcmp(type, "OSC_URL"))
+ impl.curr_url = url;
+ rEnd},
+ {"free:sb", 0, 0,
+ rBegin;
+ const char *type = rtosc_argument(msg, 0).s;
+ void *ptr = *(void**)rtosc_argument(msg, 1).b.data;
+ deallocate(type, ptr);
+ rEnd},
+ {"request_memory:", 0, 0,
+ rBegin;
+ //Generate out more memory for the RT memory pool
+ //5MBi chunk
+ size_t N = 5*1024*1024;
+ void *mem = malloc(N);
+ impl.uToB->write("/add-rt-memory", "bi", sizeof(void*), &mem, N);
+ rEnd},
+ {"setprogram:cc:ii", 0, 0,
+ rBegin;
+ const int part = rtosc_argument(msg, 0).i;
+ const int program = rtosc_argument(msg, 1).i;
+ impl.loadPart(part, impl.master->bank.ins[program].filename.c_str(), impl.master);
+ rEnd},
+ {"undo_pause:", 0, 0, rBegin; impl.recording_undo = false; rEnd},
+ {"undo_resume:", 0, 0, rBegin; impl.recording_undo = true; rEnd},
+ {"undo_change", 0, 0,
+ rBegin;
+ if(impl.recording_undo)
+ impl.undo.recordEvent(msg);
+ rEnd},
+ {"midi-use-CC:i", 0, 0,
+ rBegin;
+ impl.midi_mapper.useFreeID(rtosc_argument(msg, 0).i);
+ rEnd},
+ {"broadcast:", 0, 0, rBegin; impl.broadcast = true; rEnd},
+ {"forward:", 0, 0, rBegin; impl.forward = true; rEnd},
+};
+#undef rBegin
+#undef rEnd
+
+/******************************************************************************
+ * MiddleWare Implementation *
+ ******************************************************************************/
+
MiddleWareImpl::MiddleWareImpl(MiddleWare *mw, SYNTH_T synth_,
Config* config, int preferrred_port)
:parent(mw), config(config), ui(nullptr), synth(std::move(synth_)),
@@ -844,6 +1047,8 @@ MiddleWareImpl::MiddleWareImpl(MiddleWare *mw, SYNTH_T synth_,
{
bToU = new rtosc::ThreadLink(4096*2,1024);
uToB = new rtosc::ThreadLink(4096*2,1024);
+ midi_mapper.base_ports = &Master::ports;
+ midi_mapper.rt_cb = [this](const char *msg){handleMsg(msg);};
if(preferrred_port != -1)
server = lo_server_new_with_proto(to_s(preferrred_port).c_str(),
LO_UDP, liblo_error_cb);
@@ -862,9 +1067,6 @@ MiddleWareImpl::MiddleWareImpl(MiddleWare *mw, SYNTH_T synth_,
idle = 0;
idle_ptr = 0;
-#ifndef PLUGINVERSION
- the_bToU = bToU;
-#endif
master = new Master(synth, config);
master->bToU = bToU;
master->uToB = uToB;
@@ -891,13 +1093,8 @@ MiddleWareImpl::MiddleWareImpl(MiddleWare *mw, SYNTH_T synth_,
});
}
-void DummyDataObj::reply(const char *msg)
-{
- mwi->bToUhandle(msg, true);
-}
MiddleWareImpl::~MiddleWareImpl(void)
{
- warnMemoryLeaks();
if(server)
lo_server_free(server);
@@ -934,6 +1131,7 @@ MiddleWareImpl::~MiddleWareImpl(void)
void MiddleWareImpl::doReadOnlyOp(std::function<void()> read_only_fn)
{
+ assert(uToB);
uToB->write("/freeze_state","");
std::list<const char *> fico;
@@ -967,126 +1165,74 @@ void MiddleWareImpl::doReadOnlyOp(std::function<void()> read_only_fn)
}
}
-void MiddleWareImpl::bToUhandle(const char *rtmsg, bool dummy)
+void MiddleWareImpl::broadcastToRemote(const char *rtmsg)
{
- assert(strcmp(rtmsg, "/part0/kit0/Ppadenableda"));
- assert(strcmp(rtmsg, "/ze_state"));
- //Dump Incomming Events For Debugging
- if(strcmp(rtmsg, "/vu-meter") && false) {
- fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 1 + 30, 0 + 40);
- fprintf(stdout, "frontend: '%s'<%s>\n", rtmsg,
- rtosc_argument_string(rtmsg));
- fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
- }
+ //Always send to the local UI
+ sendToRemote(rtmsg, "GUI");
- //Activity dot
- //printf(".");fflush(stdout);
-
- if(!strcmp(rtmsg, "/echo")
- && !strcmp(rtosc_argument_string(rtmsg),"ss")
- && !strcmp(rtosc_argument(rtmsg,0).s, "OSC_URL"))
- curr_url = rtosc_argument(rtmsg,1).s;
- else if(!strcmp(rtmsg, "/free")
- && !strcmp(rtosc_argument_string(rtmsg),"sb")) {
- deallocate(rtosc_argument(rtmsg, 0).s, *((void**)rtosc_argument(rtmsg, 1).b.data));
- } else if(!strcmp(rtmsg, "/request-memory")) {
- //Generate out more memory for the RT memory pool
- //5MBi chunk
- size_t N = 5*1024*1024;
- void *mem = malloc(N);
- uToB->write("/add-rt-memory", "bi", sizeof(void*), &mem, N);
- } else if(!strcmp(rtmsg, "/setprogram")
- && !strcmp(rtosc_argument_string(rtmsg),"cc")) {
- loadPart(rtosc_argument(rtmsg,0).i, master->bank.ins[rtosc_argument(rtmsg,1).i].filename.c_str(), master);
- } else if(!strcmp(rtmsg, "/setbank")
- && !strcmp(rtosc_argument_string(rtmsg), "c")) {
- loadPendingBank(rtosc_argument(rtmsg,0).i, master->bank);
- } else if(!strcmp("/undo_pause", rtmsg)) {
- recording_undo = false;
- } else if(!strcmp("/undo_resume", rtmsg)) {
- recording_undo = true;
- } else if(!strcmp("undo_change", rtmsg) && recording_undo) {
- undo.recordEvent(rtmsg);
- } else if(!strcmp(rtmsg, "/broadcast")) {
- broadcast = true;
- } else if(broadcast) {
- broadcast = false;
-#ifdef PLUGINVERSION
- if (!curr_url.empty()) // falktx: check added
- cb(ui, rtmsg);
-
- // falktx: changed curr_url to last_url
- if(last_url != "GUI") {
- lo_message msg = lo_message_deserialise((void*)rtmsg,
- rtosc_message_length(rtmsg, bToU->buffer_size()), NULL);
-
- //Send to known url
- if(!last_url.empty()) {
- lo_address addr = lo_address_new_from_url(last_url.c_str());
- lo_send_message(addr, rtmsg, msg);
- }
- }
-#else
- cb(ui, rtmsg);
+ //Send to remote UI if there's one listening
+ if(curr_url != "GUI")
+ sendToRemote(rtmsg, curr_url);
- if(curr_url != "GUI") {
- lo_message msg = lo_message_deserialise((void*)rtmsg,
- rtosc_message_length(rtmsg, bToU->buffer_size()), NULL);
+ broadcast = false;
+}
- //Send to known url
- if(!curr_url.empty()) {
- lo_address addr = lo_address_new_from_url(curr_url.c_str());
- lo_send_message(addr, rtmsg, msg);
- }
- }
-#endif
- } else if((dummy?last_url:curr_url) == "GUI" || !strcmp(rtmsg, "/close-ui")) {
+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);
- } else{
+ } else if(!dest.empty()) {
lo_message msg = lo_message_deserialise((void*)rtmsg,
rtosc_message_length(rtmsg, bToU->buffer_size()), NULL);
//Send to known url
- if(!curr_url.empty()) {
- lo_address addr = lo_address_new_from_url(dummy?last_url.c_str():curr_url.c_str());
+ lo_address addr = lo_address_new_from_url(dest.c_str());
+ if(addr)
lo_send_message(addr, rtmsg, msg);
- }
}
}
-bool MiddleWareImpl::handleOscil(string path, const char *msg, void *v)
+/**
+ * Handle all events coming from the backend
+ *
+ * This includes forwarded events which need to be retransmitted to the backend
+ * after the snooping code inspects the message
+ */
+void MiddleWareImpl::bToUhandle(const char *rtmsg)
{
- //printf("handleOscil...\n");
- char buffer[1024];
- memset(buffer, 0, sizeof(buffer));
- DummyDataObj d(buffer, 1024, v, this, uToB);
- strcpy(buffer, path.c_str());
- if(!v)
- return true;
+ //Verify Message isn't a known corruption bug
+ assert(strcmp(rtmsg, "/part0/kit0/Ppadenableda"));
+ assert(strcmp(rtmsg, "/ze_state"));
- //Paste To Non-Realtime Parameters and then forward
- if(strstr(msg, "paste") && !strstr(msg, "padpars")) {
+ //Dump Incomming Events For Debugging
+ if(strcmp(rtmsg, "/vu-meter") && false) {
+ fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 1 + 30, 0 + 40);
+ fprintf(stdout, "frontend[%c]: '%s'<%s>\n", forward?'f':broadcast?'b':'N',
+ rtmsg, rtosc_argument_string(rtmsg));
+ fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
}
- if(!strstr(msg, "padpars")) {
- for(auto &p:OscilGen::ports.ports) {
- if(strstr(p.name,msg) && strstr(p.metadata, "realtime") &&
- !strcmp("b", rtosc_argument_string(msg))) {
- //printf("sending along packet '%s'...\n", msg);
- return false;
- }
- }
- }
+ //Activity dot
+ //printf(".");fflush(stdout);
- OscilGen::ports.dispatch(msg, d);
- if(!d.matches) {
- //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 1, 7 + 30, 0 + 40);
- //fprintf(stderr, "Unknown location '%s%s'<%s>\n",
- // path.c_str(), msg, rtosc_argument_string(msg));
- //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
+ MwDataObj d(this);
+ middlewareReplyPorts.dispatch(rtmsg, d, true);
+
+ in_order = true;
+ //Normal message not captured by the ports
+ if(d.matches == 0) {
+ if(forward) {
+ forward = false;
+ handleMsg(rtmsg);
+ } if(broadcast)
+ broadcastToRemote(rtmsg);
+ else
+ sendToCurrentRemote(rtmsg);
}
+ in_order = false;
- return true;
}
//Allocate kits on a as needed basis
@@ -1104,8 +1250,7 @@ void MiddleWareImpl::kitEnable(const char *msg)
type = 1;
else if(strstr(msg, "Psubenabled"))
type = 2;
-
- if(type == -1)
+ else
return;
const char *tmp = strstr(msg, "part");
@@ -1148,14 +1293,13 @@ void MiddleWareImpl::kitEnable(int part, int kit, int type)
uToB->write(url.c_str(), "b", sizeof(void*), &ptr);
}
-/* BASE/part#/kititem#
- * BASE/part#/kit#/adpars/voice#/oscil/\*
- * BASE/part#/kit#/adpars/voice#/mod-oscil/\*
- * BASE/part#/kit#/padpars/prepare
- * BASE/part#/kit#/padpars/oscil/\*
+
+/*
+ * Handle all messages traveling to the realtime side.
*/
void MiddleWareImpl::handleMsg(const char *msg)
{
+ //Check for known bugs
assert(msg && *msg && rindex(msg, '/')[1]);
assert(strstr(msg,"free") == NULL || strstr(rtosc_argument_string(msg), "b") == NULL);
assert(strcmp(msg, "/part0/Psysefxvol"));
@@ -1164,95 +1308,32 @@ void MiddleWareImpl::handleMsg(const char *msg)
assert(strcmp(msg, "sysefx0sysefx0/preset"));
assert(strcmp(msg, "/sysefx0preset"));
assert(strcmp(msg, "Psysefxvol0/part0"));
- //fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 6 + 30, 0 + 40);
- //fprintf(stdout, "middleware: '%s':%s\n", msg, rtosc_argument_string(msg));
- //fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
+
+ if(strcmp("/get-vu", msg) && false) {
+ fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 6 + 30, 0 + 40);
+ fprintf(stdout, "middleware: '%s':%s\n", msg, rtosc_argument_string(msg));
+ fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
+ }
+
const char *last_path = rindex(msg, '/');
- if(!last_path)
+ if(!last_path) {
+ printf("Bad message in handleMsg() <%s>\n", msg);
+ assert(false);
return;
+ }
+ MwDataObj d(this);
+ middwareSnoopPorts.dispatch(msg, d, true);
- //printf("watching '%s' go by\n", msg);
- //Get the object resource locator
- string obj_rl(msg, last_path+1);
- int npart = -1;
- char testchr = 0;
-
- std::function<void(const char*)> bank_cb;
- if(last_url == "GUI")
- bank_cb = [this](const char *msg){if(osc)osc->tryLink(msg);};
- else
- bank_cb = [this](const char *msg){if(osc)osc->tryLink(msg);this->bToUhandle(msg, 1);};
-
- if(!strcmp(msg, "/refresh_bank") && !strcmp(rtosc_argument_string(msg), "i")) {
- refreshBankView(master->bank, rtosc_argument(msg,0).i, bank_cb);
- } else if(!strcmp(msg, "/bank-list") && !strcmp(rtosc_argument_string(msg), "")) {
- bankList(master->bank, bank_cb);
- } else if(!strcmp(msg, "/rescanforbanks") && !strcmp(rtosc_argument_string(msg), "")) {
- rescanForBanks(master->bank, bank_cb);
- } else if(!strcmp(msg, "/loadbank") && !strcmp(rtosc_argument_string(msg), "i")) {
- loadBank(master->bank, rtosc_argument(msg, 0).i, bank_cb);
- } else if(!strcmp(msg, "/loadbank") && !strcmp(rtosc_argument_string(msg), "")) {
- bankPos(master->bank, bank_cb);
- } else if(obj_store.has(obj_rl)) {
- //try some over simplified pattern matching
- if(strstr(msg, "oscilgen/") || strstr(msg, "FMSmp/") || strstr(msg, "OscilSmp/")) {
- if(!handleOscil(obj_rl, last_path+1, obj_store.get(obj_rl)))
- uToB->raw_write(msg);
- //else if(strstr(obj_rl.c_str(), "kititem"))
- // handleKitItem(obj_rl, objmap[obj_rl],atoi(rindex(msg,'m')+1),rtosc_argument(msg,0).T);
- } else if(strstr(msg, "padpars/prepare"))
- preparePadSynth(obj_rl,(PADnoteParameters *) obj_store.get(obj_rl), uToB);
- else if(strstr(msg, "padpars")) {
- if(!handlePAD(obj_rl, last_path+1, obj_store.get(obj_rl)))
- uToB->raw_write(msg);
- } else //just forward the message
- uToB->raw_write(msg);
- } else if(strstr(msg, "/save_xmz") && !strcmp(rtosc_argument_string(msg), "s")) {
- saveMaster(rtosc_argument(msg,0).s);
- } else if(strstr(msg, "/save_xiz") && !strcmp(rtosc_argument_string(msg), "is")) {
- savePart(rtosc_argument(msg,0).i,rtosc_argument(msg,1).s);
- } else if(strstr(msg, "/load_xmz") && !strcmp(rtosc_argument_string(msg), "s")) {
- loadMaster(rtosc_argument(msg,0).s);
- } else if(strstr(msg, "/reset_master") && !strcmp(rtosc_argument_string(msg), "")) {
- loadMaster(NULL);
- } else if(!strcmp(msg, "/load_xiz") && !strcmp(rtosc_argument_string(msg), "is")) {
- pending_load[rtosc_argument(msg,0).i]++;
- loadPart(rtosc_argument(msg,0).i, rtosc_argument(msg,1).s, master);
- } else if(strstr(msg, "load-part") && !strcmp(rtosc_argument_string(msg), "is")) {
- pending_load[rtosc_argument(msg,0).i]++;
- loadPart(rtosc_argument(msg,0).i, rtosc_argument(msg,1).s, master);
- } else if(!strcmp(msg, "/setprogram")
- && !strcmp(rtosc_argument_string(msg),"c")) {
- pending_load[0]++;
- loadPart(0, master->bank.ins[rtosc_argument(msg,0).i].filename.c_str(), master);
- } else if(strstr(msg, "save-bank-part") && !strcmp(rtosc_argument_string(msg), "ii")) {
- saveBankSlot(rtosc_argument(msg,0).i, rtosc_argument(msg,1).i, master);
- } else if(strstr(msg, "bank-rename") && !strcmp(rtosc_argument_string(msg), "is")) {
- renameBankSlot(rtosc_argument(msg,0).i, rtosc_argument(msg,1).s, master);
- } else if(strstr(msg, "swap-bank-slots") && !strcmp(rtosc_argument_string(msg), "ii")) {
- swapBankSlot(rtosc_argument(msg,0).i, rtosc_argument(msg,1).i, master);
- } else if(strstr(msg, "clear-bank-slot") && !strcmp(rtosc_argument_string(msg), "i")) {
- clearBankSlot(rtosc_argument(msg,0).i, master);
- } else if(strstr(msg, "/config/")) {
- handleConfig(msg);
- } else if(strstr(msg, "/presets/")) {
- handlePresets(msg);
- } else if(strstr(msg, "/io/")) {
- handleIo(msg);
- } else if(strstr(msg, "Padenabled") || strstr(msg, "Ppadenabled") || strstr(msg, "Psubenabled")) {
- kitEnable(msg);
- uToB->raw_write(msg);
- } else if(sscanf(msg, "/part%d/clea%c", &npart, &testchr) == 2 && testchr == 'r') {
- loadClearPart(npart);
- } else if(!strcmp(msg, "/undo")) {
- undo.seekHistory(-1);
- } else if(!strcmp(msg, "/redo")) {
- undo.seekHistory(+1);
- } else if(!strcmp(msg, "/ui/title")) {
- ;//drop the message into the abyss
- } else
+ //A message unmodified by snooping
+ if(d.matches == 0 || d.forwarded) {
+ //if(strcmp("/get-vu", msg)) {
+ // printf("Message Continuing on<%s:%s>...\n", msg, rtosc_argument_string(msg));
+ //}
uToB->raw_write(msg);
+ } else {
+ //printf("Message Handled<%s:%s>...\n", msg, rtosc_argument_string(msg));
+ }
}
void MiddleWareImpl::write(const char *path, const char *args, ...)
@@ -1278,9 +1359,6 @@ void MiddleWareImpl::write(const char *path, const char *args, va_list va)
warnx("Failed to write message to '%s'", path);
}
-void MiddleWareImpl::warnMemoryLeaks(void)
-{}
-
/******************************************************************************
* MidleWare Forwarding Stubs *
******************************************************************************/
@@ -1298,6 +1376,8 @@ void MiddleWare::updateResources(Master *m)
}
Master *MiddleWare::spawnMaster(void)
{
+ assert(impl->master);
+ assert(impl->master->uToB);
return impl->master;
}
Fl_Osc_Interface *MiddleWare::spawnUiApi(void)
@@ -1343,7 +1423,7 @@ void MiddleWare::transmitMsg(const char *path, const char *args, ...)
va_end(va);
}
-void MiddleWare::transmitMsg(const char *path, const char *args, va_list va)
+void MiddleWare::transmitMsg_va(const char *path, const char *args, va_list va)
{
char buffer[1024];
if(rtosc_vmessage(buffer, 1024, path, args, va))
diff --git a/src/Misc/MiddleWare.h b/src/Misc/MiddleWare.h
@@ -32,11 +32,15 @@ class MiddleWare
//Handle a rtosc Message uToB
void transmitMsg(const char *, const char *args, ...);
//Handle a rtosc Message uToB
- void transmitMsg(const char *, const char *args, va_list va);
+ void transmitMsg_va(const char *, const char *args, va_list va);
+
//Indicate that a bank will be loaded
+ //NOTE: Can only be called by realtime thread
void pendingSetBank(int bank);
//Indicate that a program will be loaded on a known part
+ //NOTE: Can only be called by realtime thread
void pendingSetProgram(int part, int program);
+
//Get/Set the active bToU url
std::string activeUrl(void);
void activeUrl(std::string u);
diff --git a/src/Misc/PresetExtractor.cpp b/src/Misc/PresetExtractor.cpp
@@ -32,6 +32,7 @@ const rtosc::Ports real_preset_ports =
{"scan-for-presets:", 0, 0,
[](const char *, rtosc::RtData &d) {
MiddleWare &mw = *(MiddleWare*)d.obj;
+ assert(d.obj);
mw.getPresetsStore().scanforpresets();
auto &pre = mw.getPresetsStore().presets;
d.reply(d.loc, "i", pre.size());
@@ -45,6 +46,7 @@ const rtosc::Ports real_preset_ports =
{"copy:s:ss:si:ssi", 0, 0,
[](const char *msg, rtosc::RtData &d) {
MiddleWare &mw = *(MiddleWare*)d.obj;
+ assert(d.obj);
std::string args = rtosc_argument_string(msg);
d.reply(d.loc, "s", "clipboard copy...");
printf("\nClipboard Copy...\n");
@@ -65,6 +67,7 @@ const rtosc::Ports real_preset_ports =
{"paste:s:ss:si:ssi", 0, 0,
[](const char *msg, rtosc::RtData &d) {
MiddleWare &mw = *(MiddleWare*)d.obj;
+ assert(d.obj);
std::string args = rtosc_argument_string(msg);
d.reply(d.loc, "s", "clipboard paste...");
printf("\nClipboard Paste...\n");
@@ -85,11 +88,13 @@ const rtosc::Ports real_preset_ports =
{"clipboard-type:", 0, 0,
[](const char *, rtosc::RtData &d) {
const MiddleWare &mw = *(MiddleWare*)d.obj;
+ assert(d.obj);
d.reply(d.loc, "s", mw.getPresetsStore().clipboard.type.c_str());
}},
{"delete:s", 0, 0,
[](const char *msg, rtosc::RtData &d) {
MiddleWare &mw = *(MiddleWare*)d.obj;
+ assert(d.obj);
mw.getPresetsStore().deletepreset(rtosc_argument(msg,0).s);
}},
@@ -133,6 +138,7 @@ class Capture:public rtosc::RtData
{
matches = 0;
memset(locbuf, 0, sizeof(locbuf));
+ memset(msgbuf, 0, sizeof(msgbuf));
loc = locbuf;
loc_size = sizeof(locbuf);
obj = obj_;
@@ -193,7 +199,9 @@ std::string doCopy(MiddleWare &mw, string url, string name)
mw.doReadOnlyOp([&xml, url, name, &mw](){
Master *m = mw.spawnMaster();
//Get the pointer
+ printf("capture at <%s>\n", (url+"self").c_str());
T *t = (T*)capture<void*>(m, url+"self");
+ assert(t);
//Extract Via mxml
//t->add2XML(&xml);
t->copy(mw.getPresetsStore(), name.empty()? NULL:name.c_str());
@@ -303,6 +311,7 @@ void doClassPaste(std::string type, std::string type_, MiddleWare &mw, string ur
std::string doClassCopy(std::string type, MiddleWare &mw, string url, string name)
{
+ printf("doClassCopy(%p)\n", mw.spawnMaster()->uToB);
if(type == "EnvelopeParams")
return doCopy<EnvelopeParams>(mw, url, name);
else if(type == "LFOParams")
diff --git a/src/Misc/XMLwrapper.cpp b/src/Misc/XMLwrapper.cpp
@@ -632,3 +632,55 @@ mxml_node_t *XMLwrapper::addparams(const char *name, unsigned int params,
}
return element;
}
+
+XmlNode::XmlNode(std::string name_)
+ :name(name_)
+{}
+
+std::string &XmlNode::operator[](std::string name)
+{
+ //fetch an existing one
+ for(auto &a:attrs)
+ if(a.name == name)
+ return a.value;
+
+ //create a new one
+ attrs.push_back({name, ""});
+ return attrs[attrs.size()-1].value;
+}
+
+bool XmlNode::has(std::string name_)
+{
+ //fetch an existing one
+ for(auto &a:attrs)
+ if(a.name == name_)
+ return true;
+ return false;
+}
+
+void XMLwrapper::add(const XmlNode &node_)
+{
+ mxml_node_t *element = mxmlNewElement(node, node_.name.c_str());
+ for(auto attr:node_.attrs)
+ mxmlElementSetAttr(element, attr.name.c_str(),
+ attr.value.c_str());
+}
+
+std::vector<XmlNode> XMLwrapper::getBranch(void) const
+{
+ std::vector<XmlNode> res;
+ mxml_node_t *current = node->child;
+ while(current) {
+ if(current->type == MXML_ELEMENT) {
+ auto elm = current->value.element;
+ XmlNode n(elm.name);
+ for(int i=0; i<elm.num_attrs; ++i) {
+ auto &attr = elm.attrs[i];
+ n[attr.name] = attr.value;
+ }
+ res.push_back(n);
+ }
+ current = mxmlWalkNext(current, node, MXML_NO_DESCEND);
+ }
+ return res;
+}
diff --git a/src/Misc/XMLwrapper.h b/src/Misc/XMLwrapper.h
@@ -24,13 +24,30 @@
#include <mxml.h>
#include <string>
-#ifndef float
-#define float float
-#endif
+#include <vector>
#ifndef XML_WRAPPER_H
#define XML_WRAPPER_H
+class XmlAttr
+{
+ public:
+ std::string name;
+ std::string value;
+};
+
+
+class XmlNode
+{
+ public:
+ XmlNode(std::string name_);
+ std::string name;
+ std::vector<XmlAttr> attrs;
+
+ std::string &operator[](std::string name);
+ bool has(std::string);
+};
+
/**Mxml wrapper*/
class XMLwrapper
{
@@ -220,6 +237,10 @@ class XMLwrapper
*/
bool hasPadSynth() const;
+ void add(const XmlNode &node);
+
+ std::vector<XmlNode> getBranch(void) const;
+
private:
/**
diff --git a/src/Params/ADnoteParameters.cpp b/src/Params/ADnoteParameters.cpp
@@ -42,8 +42,27 @@ using rtosc::RtData;
#define rObject ADnoteVoiceParam
static const Ports voicePorts = {
- rRecurp(OscilSmp, "Primary Oscillator"),
- rRecurp(FMSmp, "Modulating Oscillator"),
+ //Send Messages To Oscillator Realtime Table
+ {"OscilSmp/", rDoc("Primary Oscillator"),
+ &OscilGen::ports,
+ rBOIL_BEGIN
+ if(obj->OscilSmp == NULL) return;
+ data.obj = obj->OscilSmp;
+ SNIP
+ OscilGen::realtime_ports.dispatch(msg, data);
+ if(data.matches == 0)
+ data.forward();
+ rBOIL_END},
+ {"FMSmp/", rDoc("Modulating Oscillator"),
+ &OscilGen::ports,
+ rBOIL_BEGIN
+ if(obj->FMSmp == NULL) return;
+ data.obj = obj->FMSmp;
+ SNIP
+ OscilGen::realtime_ports.dispatch(msg, data);
+ if(data.matches == 0)
+ data.forward();
+ rBOIL_END},
rRecurp(FreqLfo, "Frequency LFO"),
rRecurp(AmpLfo, "Amplitude LFO"),
rRecurp(FilterLfo, "Filter LFO"),
@@ -107,7 +126,7 @@ static const Ports voicePorts = {
rToggle(PFMFreqEnvelopeEnabled, "Modulator Frequency Envelope"),
rToggle(PFMAmpEnvelopeEnabled, "Modulator Amplitude Envelope"),
-
+
//weird stuff for PCoarseDetune
{"detunevalue:", rMap(unit,cents) rDoc("Get detune in cents"), NULL,
[](const char *, RtData &d)
@@ -148,7 +167,7 @@ static const Ports voicePorts = {
obj->PCoarseDetune = k + (obj->PCoarseDetune/1024)*1024;
}
}},
-
+
//weird stuff for PCoarseDetune
{"FMdetunevalue:", rMap(unit,cents) rDoc("Get modulator detune"), NULL, [](const char *, RtData &d)
{
diff --git a/src/Params/PADnoteParameters.cpp b/src/Params/PADnoteParameters.cpp
@@ -34,113 +34,41 @@
using namespace rtosc;
-#define PC(x) rParamZyn(P##x, "undocumented padnote parameter")
-
-template<int i>
-void simpleset(const char *m, rtosc::RtData &d)
-{
- unsigned char *addr = ((unsigned char*) d.obj)+i;
- if(!rtosc_narguments(m))
- d.reply(d.loc, "c", *addr);
- else
- *addr = rtosc_argument(m, 0).i;
-}
-
#define rObject PADnoteParameters
-
-#define P_C(x) rtosc::Port{#x "::c", "::", NULL, \
- simpleset<__builtin_offsetof(class PADnoteParameters, P##x)>}
-static const rtosc::Ports PADnotePorts =
+static const rtosc::Ports realtime_ports =
{
- rRecurp(oscilgen, "Oscillator"),
rRecurp(FreqLfo, "Frequency LFO"),
rRecurp(AmpLfo, "Amplitude LFO"),
rRecurp(FilterLfo, "Filter LFO"),
- rRecurp(resonance, "Resonance"),
rRecurp(FreqEnvelope, "Frequency Envelope"),
rRecurp(AmpEnvelope, "Amplitude Envelope"),
rRecurp(FilterEnvelope, "Filter Envelope"),
rRecurp(GlobalFilter, "Post Filter"),
- rParamI(Pmode, rMap(min, 0), rMap(max, 2), "0 - bandwidth, 1 - discrete 2 - continious"),
- PC(Volume),
- PC(hp.base.type),
- PC(hp.base.par1),
- PC(hp.freqmult),
- PC(hp.modulator.par1),
- PC(hp.modulator.freq),
- PC(hp.width),
- PC(hp.amp.mode),
- PC(hp.amp.type),
- PC(hp.amp.par1),
- PC(hp.amp.par2),
- rToggle(Php.autoscale, "Autoscaling Harmonics"),
- PC(hp.onehalf),
-
- PC(bwscale),
-
- PC(hrpos.type),
- PC(hrpos.par1),
- PC(hrpos.par2),
- PC(hrpos.par3),
-
- PC(quality.samplesize),
- PC(quality.basenote),
- PC(quality.oct),
- PC(quality.smpoct),
-
- PC(fixedfreq),
- PC(fixedfreqET),
- PC(Stereo),
- PC(Panning),
- PC(AmpVelocityScaleFunction),
- PC(PunchStrength),
- PC(PunchTime),
- PC(PunchStretch),
- PC(PunchVelocitySensing),
- PC(FilterVelocityScale),
- PC(FilterVelocityScaleFunction),
+
+ //Volume
+ rToggle(PStereo, "Stereo/Mono Mode"),
+ rParamZyn(PPanning, "Left Right Panning"),
+ rParamZyn(PVolume, "Synth Volume"),
+ rParamZyn(PAmpVelocityScaleFunction, "Amplitude Velocity Sensing function"),
+
+ //Punch
+ rParamZyn(PPunchStrength, "Punch Strength"),
+ rParamZyn(PPunchTime, "UNKNOWN"),
+ rParamZyn(PPunchStretch, "How Punch changes with note frequency"),
+ rParamZyn(PPunchVelocitySensing, "Punch Velocity control"),
+
+ //Filter
+ rParamZyn(PFilterVelocityScale, "Filter Velocity Magnitude"),
+ rParamZyn(PFilterVelocityScaleFunction, "Filter Velocity Function Shape"),
+
+ //Freq
+ rToggle(Pfixedfreq, "Base frequency fixed frequency enable"),
+ rParamZyn(PfixedfreqET, "Equal temeperate control for fixed frequency operation"),
rParamI(PDetune, "Fine Detune"),
rParamI(PCoarseDetune, "Coarse Detune"),
rParamZyn(PDetuneType, "Magnitude of Detune"),
- {"Pbandwidth::i", rProp(parameter) rDoc("Bandwith Of Harmonics"), NULL,
- [](const char *msg, rtosc::RtData &d) {
- PADnoteParameters *p = ((PADnoteParameters*)d.obj);
- if(rtosc_narguments(msg)) {
- p->setPbandwidth(rtosc_argument(msg, 0).i);
- } else {
- d.reply(d.loc, "i", p->Pbandwidth);
- }}},
-
- {"bandwidthvalue:", rMap(unit, cents) rDoc("Get Bandwidth"), NULL,
- [](const char *, rtosc::RtData &d) {
- PADnoteParameters *p = ((PADnoteParameters*)d.obj);
- d.reply(d.loc, "f", p->setPbandwidth(p->Pbandwidth));
- }},
-
-
- {"nhr:", rProp(non-realtime) rDoc("Returns the harmonic shifts"),
- NULL, [](const char *, rtosc::RtData &d) {
- PADnoteParameters *p = ((PADnoteParameters*)d.obj);
- const unsigned n = p->synth.oscilsize / 2;
- float *tmp = new float[n];
- *tmp = 0;
- for(unsigned i=1; i<n; ++i)
- tmp[i] = p->getNhr(i);
- d.reply(d.loc, "b", n*sizeof(float), tmp);
- delete[] tmp;}},
- {"profile:i", rProp(non-realtime) rDoc("UI display of the harmonic profile"),
- NULL, [](const char *m, rtosc::RtData &d) {
- PADnoteParameters *p = ((PADnoteParameters*)d.obj);
- const int n = rtosc_argument(m, 0).i;
- if(n<=0)
- return;
- float *tmp = new float[n];
- float realbw = p->getprofile(tmp, n);
- d.reply(d.loc, "b", n*sizeof(float), tmp);
- d.reply(d.loc, "i", realbw);
- delete[] tmp;}},
{"sample#64:ifb", rProp(internal) rDoc("Nothing to see here"), 0,
[](const char *m, rtosc::RtData &d)
{
@@ -189,9 +117,118 @@ static const rtosc::Ports PADnotePorts =
obj->PCoarseDetune = k + (obj->PCoarseDetune/1024)*1024;
}
}},
+
+};
+static const rtosc::Ports non_realtime_ports =
+{
+ //Harmonic Source Distribution
+ rRecurp(oscilgen, "Oscillator"),
+ rRecurp(resonance, "Resonance"),
+
+ //Harmonic Shape
+ rOption(Pmode, rMap(min, 0), rMap(max, 2), rOptions(bandwidth,discrete,continious),
+ "Harmonic Distribution Model"),
+ rOption(Php.base.type, rOptions(Gaussian, Rectanglar, Double Exponential),
+ "Harmonic profile shape"),
+ rParamZyn(Php.base.par1, "Harmonic shape distribution parameter"),
+ rParamZyn(Php.freqmult, "Frequency multiplier on distribution"),
+ rParamZyn(Php.modulator.par1, "Distribution modulator parameter"),
+ rParamZyn(Php.modulator.freq, "Frequency of modulator parameter"),
+ rParamZyn(Php.width, "Width of base harmonic"),
+ rOption(Php.amp.mode, rOptions(Sum, Mult, Div1, Div2),
+ "Amplitude harmonic multiplier type"),
+
+ //Harmonic Modulation
+ rOption(Php.amp.type, rOptions(Off, Gauss, Sine, Flat),
+ "Type of amplitude multipler"),
+ rParamZyn(Php.amp.par1, "Amplitude multiplier parameter"),
+ rParamZyn(Php.amp.par2, "Amplitude multiplier parameter"),
+ rToggle(Php.autoscale, "Autoscaling Harmonics"),
+ rOption(Php.onehalf,
+ rOptions(Full, Upper Half, Lower Half),
+ "Harmonic cutoff model"),
+
+ //Harmonic Bandwidth
+ rOption(Pbwscale,
+ rOptions(Normal,
+ EqualHz, Quater,
+ Half, 75%, 150%,
+ Double, Inv. Half),
+ "Bandwidth scaling"),
+
+ //Harmonic Position Modulation
+ rOption(Phrpos.type,
+ rOptions(Harmonic, ShiftU, ShiftL, PowerU, PowerL, Sine,
+ Power, Shift),
+ "Harmonic Overtone shifting mode"),
+ rParamZyn(Phrpos.par1, "Harmonic position parameter"),
+ rParamZyn(Phrpos.par2, "Harmonic position parameter"),
+ rParamZyn(Phrpos.par3, "Harmonic position parameter"),
+
+ //Quality
+ rOption(Pquality.samplesize,
+ rOptions(16k (Tiny), 32k, 64k (Small), 128k,
+ 256k (Normal), 512k, 1M (Big)),
+ "Size of each wavetable element"),
+ rOption(Pquality.basenote,
+ rOptions( C-2, G-2, C-3, G-3, C-4,
+ G-4, C-5, G-5, G-6,),
+ "Base note for wavetable"),
+ rOption(Pquality.smpoct,
+ rOptions(0.5, 1, 2, 3, 4, 6, 12),
+ "Samples per octave"),
+ rParamI(Pquality.oct, rLinear(0,7),
+ "Number of octaves to sample (above the first sample"),
+
+ {"Pbandwidth::i", rProp(parameter) rLinear(0,1000) rDoc("Bandwith Of Harmonics"), NULL,
+ [](const char *msg, rtosc::RtData &d) {
+ PADnoteParameters *p = ((PADnoteParameters*)d.obj);
+ if(rtosc_narguments(msg)) {
+ p->setPbandwidth(rtosc_argument(msg, 0).i);
+ } else {
+ d.reply(d.loc, "i", p->Pbandwidth);
+ }}},
+
+ {"bandwidthvalue:", rMap(unit, cents) rDoc("Get Bandwidth"), NULL,
+ [](const char *, rtosc::RtData &d) {
+ PADnoteParameters *p = ((PADnoteParameters*)d.obj);
+ d.reply(d.loc, "f", p->setPbandwidth(p->Pbandwidth));
+ }},
+
+
+ {"nhr:", rProp(non-realtime) rDoc("Returns the harmonic shifts"),
+ NULL, [](const char *, rtosc::RtData &d) {
+ PADnoteParameters *p = ((PADnoteParameters*)d.obj);
+ const unsigned n = p->synth.oscilsize / 2;
+ float *tmp = new float[n];
+ *tmp = 0;
+ for(unsigned i=1; i<n; ++i)
+ tmp[i] = p->getNhr(i);
+ d.reply(d.loc, "b", n*sizeof(float), tmp);
+ delete[] tmp;}},
+ {"profile:i", rProp(non-realtime) rDoc("UI display of the harmonic profile"),
+ NULL, [](const char *m, rtosc::RtData &d) {
+ PADnoteParameters *p = ((PADnoteParameters*)d.obj);
+ const int n = rtosc_argument(m, 0).i;
+ if(n<=0)
+ return;
+ float *tmp = new float[n];
+ float realbw = p->getprofile(tmp, n);
+ d.reply(d.loc, "b", n*sizeof(float), tmp);
+ d.reply(d.loc, "i", realbw);
+ delete[] tmp;}},
+};
+
+const rtosc::Ports &PADnoteParameters::non_realtime_ports = ::non_realtime_ports;
+const rtosc::Ports &PADnoteParameters::realtime_ports = ::realtime_ports;
+
+
+const rtosc::MergePorts PADnoteParameters::ports =
+{
+ &non_realtime_ports,
+ &realtime_ports
};
-const rtosc::Ports &PADnoteParameters::ports = PADnotePorts;
PADnoteParameters::PADnoteParameters(const SYNTH_T &synth_, FFTwrapper *fft_)
:Presets(), synth(synth_)
diff --git a/src/Params/PADnoteParameters.h b/src/Params/PADnoteParameters.h
@@ -165,7 +165,9 @@ class PADnoteParameters:public Presets
void sampleGenerator(PADnoteParameters::callback cb,
std::function<bool()> do_abort);
- static const rtosc::Ports &ports;
+ static const rtosc::MergePorts ports;
+ static const rtosc::Ports &non_realtime_ports;
+ static const rtosc::Ports &realtime_ports;
private:
void generatespectrum_bandwidthMode(float *spectrum,
diff --git a/src/Synth/OscilGen.cpp b/src/Synth/OscilGen.cpp
@@ -38,10 +38,8 @@
pthread_t main_thread;
-#define PC(x) rParamZyn(P##x, "undocumented oscilgen parameter")
-
#define rObject OscilGen
-const rtosc::Ports OscilGen::ports = {
+const rtosc::Ports OscilGen::non_realtime_ports = {
rSelf(OscilGen),
rPaste,
//TODO ensure min/max
@@ -51,21 +49,10 @@ const rtosc::Ports OscilGen::ports = {
dB scale (-100)),
"Type of magnitude for harmonics"),
rOption(Pcurrentbasefunc,
- rOptions(sine, triangle,
- pulse,
- saw,
- power,
- gauss,
- diode,
- abssine,
- pulsesine,
- stretchsine,
- chirp,
- absstretchsine,
- chebyshev,
- sqr,
- spike,
- circle), rOpt(127,use-as-base waveform),
+ rOptions(sine, triangle, pulse, saw, power, gauss,
+ diode, abssine, pulsesine, stretchsine,
+ chirp, absstretchsine, chebyshev, sqr,
+ spike, circle), rOpt(127,use-as-base waveform),
"Base Waveform for harmonics"),
rParamZyn(Pbasefuncpar,
"Morph between possible base function shapes "
@@ -82,58 +69,32 @@ const rtosc::Ports OscilGen::ports = {
rParamZyn(Pwaveshaping, "Degree Of Waveshaping"),
rOption(Pwaveshapingfunction,
rOptions(Undistorted,
- Arctangent,
- Asymmetric,
- Pow,
- Sine,
- Quantisize,
- Zigzag,
- Limiter,
- Upper Limiter,
- Lower Limiter,
- Inverse Limiter,
- Clip,
- Asym2,
- Pow2,
- sigmoid), "Shape of distortion to be applied"),
+ Arctangent, Asymmetric, Pow, Sine, Quantisize,
+ Zigzag, Limiter, Upper Limiter, Lower Limiter,
+ Inverse Limiter, Clip, Asym2, Pow2, sigmoid),
+ "Shape of distortion to be applied"),
rOption(Pfiltertype, rOptions(No Filter,
lp, hp1, hp1b, bp1, bs1, lp2, hp2, bp2, bs2,
cos, sin, low_shelf, s), "Harmonic Filter"),
- PC(filterpar1),
- PC(filterpar2),
+ rParamZyn(Pfilterpar1, "Filter parameter"),
+ rParamZyn(Pfilterpar2, "Filter parameter"),
rToggle(Pfilterbeforews, "Filter before waveshaping spectra;"
"When enabled oscilfilter(freqs); then waveshape(freqs);, "
"otherwise waveshape(freqs); then oscilfilter(freqs);"),
- PC(satype),
+ rOption(Psatype, rOptions(None, Pow, ThrsD, ThrsU),
+ "Spectral Adjustment Type"),
rParamZyn(Psapar, "Spectral Adjustment Parameter"),
rParamI(Pharmonicshift, "Amount of shift on harmonics"),
rToggle(Pharmonicshiftfirst, "If harmonics are shifted before waveshaping/filtering"),
rOption(Pmodulation, rOptions(None, Rev, Sine, Power),
"Frequency Modulation To Combined Spectra"),
- rParamZyn(Pmodulationpar1,
- "modulation parameter"),
- rParamZyn(Pmodulationpar2,
- "modulation parameter"),
- rParamZyn(Pmodulationpar3,
- "modulation parameter"),
- //FIXME realtime parameters lurking below
- PC(rand),
- rParamZyn(Pamprandpower,
- "Variance of harmonic randomness"),
- rOption(Pamprandtype, rOptions(None, Pow, Sin),
- "Harmonic random distribution to select from"),
- rOption(Padaptiveharmonics,
- rOptions(OFF, ON, Square, 2xSub, 2xAdd, 3xSub, 3xAdd, 4xSub, 4xAdd),
- "Adaptive Harmonics Mode"),
- rParamI(Padaptiveharmonicsbasefreq, rLinear(0,255),
- "Base frequency of adaptive harmonic (30..3000Hz)"),
- rParamI(Padaptiveharmonicspower,rLinear(0,200),
- "Adaptive Harmonic Strength"),
- rParamZyn(Padaptiveharmonicspar,
- "Adaptive Harmonics Postprocessing Power"),
+ rParamZyn(Pmodulationpar1, "modulation parameter"),
+ rParamZyn(Pmodulationpar2, "modulation parameter"),
+ rParamZyn(Pmodulationpar3, "modulation parameter"),
+
//TODO update to rArray and test
- {"phase#128::c", rProp(parameter) rDoc("Sets harmonic phase"),
+ {"phase#128::c:i", rProp(parameter) rLinear(0,127) rDoc("Sets harmonic phase"),
NULL, [](const char *m, rtosc::RtData &d) {
const char *mm = m;
while(*mm && !isdigit(*mm)) ++mm;
@@ -144,7 +105,7 @@ const rtosc::Ports OscilGen::ports = {
phase = rtosc_argument(m,0).i;
}},
//TODO update to rArray and test
- {"magnitude#128::c", rProp(parameter) rDoc("Sets harmonic magnitude"),
+ {"magnitude#128::c:i", rProp(parameter) rLinear(0,127) rDoc("Sets harmonic magnitude"),
NULL, [](const char *m, rtosc::RtData &d) {
//printf("I'm at '%s'\n", d.loc);
const char *mm = m;
@@ -152,8 +113,20 @@ const rtosc::Ports OscilGen::ports = {
unsigned char &mag = ((OscilGen*)d.obj)->Phmag[atoi(mm)];
if(!rtosc_narguments(m))
d.reply(d.loc, "c", mag);
- else
+ else {
mag = rtosc_argument(m,0).i;
+ printf("setting magnitude\n\n");
+ //XXX hack hack
+ char *repath = strdup(d.loc);
+ char *edit = rindex(repath, '/')+1;
+ strcpy(edit, "prepare");
+ OscilGen &o = *((OscilGen*)d.obj);
+ fft_t *data = new fft_t[o.synth.oscilsize / 2];
+ o.prepare(data);
+ fprintf(stderr, "sending '%p' of fft data\n", data);
+ d.chain(repath, "b", sizeof(fft_t*), &data);
+ o.pendingfreqs = data;
+ }
}},
{"base-spectrum:", rProp(non-realtime) rDoc("Returns spectrum of base waveshape"),
NULL, [](const char *, rtosc::RtData &d) {
@@ -203,8 +176,8 @@ const rtosc::Ports OscilGen::ports = {
OscilGen &o = *(OscilGen*)d.obj;
fft_t *data = new fft_t[o.synth.oscilsize / 2];
o.prepare(data);
- //fprintf(stderr, "sending '%p' of fft data\n", data);
- d.reply("/forward", "sb", d.loc, sizeof(fft_t*), &data);
+ fprintf(stderr, "sending '%p' of fft data\n", data);
+ d.chain(d.loc, "b", sizeof(fft_t*), &data);
o.pendingfreqs = data;
}},
{"convert2sine:", rProp(non-realtime) rDoc("Translates waveform into FS"),
@@ -214,20 +187,42 @@ const rtosc::Ports OscilGen::ports = {
{"use-as-base:", rProp(non-realtime) rDoc("Translates current waveform into base"),
NULL, [](const char *, rtosc::RtData &d) {
((OscilGen*)d.obj)->useasbase();
- }},
- {"prepare:b", rProp(internal) rProp(non-realtime) rProp(pointer) rDoc("Sets prepared fft data"),
+ }}};
+
+#define rForwardCb [](const char *msg, rtosc::RtData &d) {\
+ printf("fowarding...\n"); d.forward();}
+const rtosc::Ports OscilGen::realtime_ports{
+ rSelf(OscilGen),
+ rParamZyn(Prand, "Oscilator Phase Randomness: smaller than 0 is \""
+ "group\", larger than 0 is for each harmonic"),
+ rParamZyn(Pamprandpower,
+ "Variance of harmonic randomness"),
+ rOption(Pamprandtype, rOptions(None, Pow, Sin),
+ "Harmonic random distribution to select from"),
+ rOption(Padaptiveharmonics,
+ rOptions(OFF, ON, Square, 2xSub, 2xAdd, 3xSub, 3xAdd, 4xSub, 4xAdd),
+ "Adaptive Harmonics Mode"),
+ rParamI(Padaptiveharmonicsbasefreq, rLinear(0,255),
+ "Base frequency of adaptive harmonic (30..3000Hz)"),
+ rParamI(Padaptiveharmonicspower,rLinear(0,200),
+ "Adaptive Harmonic Strength"),
+ rParamZyn(Padaptiveharmonicspar,
+ "Adaptive Harmonics Postprocessing Power"),
+ {"prepare:b", rProp(internal) rProp(realtime) rProp(pointer) rDoc("Sets prepared fft data"),
NULL, [](const char *m, rtosc::RtData &d) {
- //fprintf(stderr, "prepare:b got a message from '%s'\n", m);
+ fprintf(stderr, "prepare:b got a message from '%s'\n", m);
OscilGen &o = *(OscilGen*)d.obj;
assert(rtosc_argument(m,0).b.len == sizeof(void*));
d.reply("/free", "sb", "fft_t", sizeof(void*), &o.oscilFFTfreqs);
- //fprintf(stderr, "\n\n");
- //fprintf(stderr, "The ID of this of this thread is: %ld\n", (long)pthread_self());
- //fprintf(stderr, "o.oscilFFTfreqs = %p\n", o.oscilFFTfreqs);
- assert(main_thread != pthread_self());
assert(o.oscilFFTfreqs !=*(fft_t**)rtosc_argument(m,0).b.data);
o.oscilFFTfreqs = *(fft_t**)rtosc_argument(m,0).b.data;
}},
+
+};
+
+const rtosc::MergePorts OscilGen::ports{
+ &OscilGen::realtime_ports,
+ &OscilGen::non_realtime_ports
};
diff --git a/src/Synth/OscilGen.h b/src/Synth/OscilGen.h
@@ -114,7 +114,9 @@ class OscilGen:public Presets
bool ADvsPAD; //if it is used by ADsynth or by PADsynth
- static const rtosc::Ports ports;
+ static const rtosc::MergePorts ports;
+ static const rtosc::Ports non_realtime_ports;
+ static const rtosc::Ports realtime_ports;
/* Oscillator Frequencies -
* this is different than the hamonics set-up by the user,
diff --git a/src/Tests/CMakeLists.txt b/src/Tests/CMakeLists.txt
@@ -14,6 +14,7 @@ CXXTEST_ADD_TEST(RandTest RandTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RandTest.h)
CXXTEST_ADD_TEST(PADnoteTest PadNoteTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/PadNoteTest.h)
CXXTEST_ADD_TEST(PluginTest PluginTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/PluginTest.h)
CXXTEST_ADD_TEST(MiddlewareTest MiddlewareTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MiddlewareTest.h)
+CXXTEST_ADD_TEST(MessageTest MessageTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MessageTest.h)
CXXTEST_ADD_TEST(UnisonTest UnisonTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/UnisonTest.h)
#CXXTEST_ADD_TEST(RtAllocTest RtAllocTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RtAllocTest.h)
CXXTEST_ADD_TEST(AllocatorTest AllocatorTest.cpp
@@ -41,6 +42,9 @@ target_link_libraries(PluginTest zynaddsubfx_core zynaddsubfx_nio
target_link_libraries(MiddlewareTest zynaddsubfx_core zynaddsubfx_nio
zynaddsubfx_gui_bridge
${GUI_LIBRARIES} ${NIO_LIBRARIES} ${AUDIO_LIBRARIES})
+target_link_libraries(MessageTest zynaddsubfx_core zynaddsubfx_nio
+ zynaddsubfx_gui_bridge
+ ${GUI_LIBRARIES} ${NIO_LIBRARIES} ${AUDIO_LIBRARIES})
target_link_libraries(UnisonTest ${test_lib})
#target_link_libraries(RtAllocTest ${test_lib})
target_link_libraries(AllocatorTest ${test_lib})
diff --git a/src/Tests/MessageTest.h b/src/Tests/MessageTest.h
@@ -0,0 +1,212 @@
+/*
+ ZynAddSubFX - a software synthesizer
+
+ PluginTest.h - CxxTest for embedding zyn
+ Copyright (C) 2013-2013 Mark McCurry
+ Authors: Mark McCurry
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License (version 2 or later) for more details.
+
+ You should have received a copy of the GNU General Public License (version 2)
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+#include <cxxtest/TestSuite.h>
+#include <cmath>
+#include <cstdlib>
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <thread>
+#include <rtosc/thread-link.h>
+#include <unistd.h>
+#include "../Misc/MiddleWare.h"
+#include "../Misc/Master.h"
+#include "../Misc/Part.h"
+#include "../Misc/PresetExtractor.h"
+#include "../Misc/PresetExtractor.cpp"
+#include "../Misc/Util.h"
+#include "../globals.h"
+#include "../UI/NSM.H"
+class NSM_Client *nsm = 0;
+MiddleWare *middleware = 0;
+
+using namespace std;
+
+char *instance_name=(char*)"";
+
+#define NUM_MIDDLEWARE 3
+
+class MessageTest:public CxxTest::TestSuite
+{
+ public:
+ Config config;
+ void setUp() {
+ synth = new SYNTH_T;
+ mw = new MiddleWare(std::move(*synth), &config);
+ ms = mw->spawnMaster();
+ realtime = NULL;
+ }
+
+ void tearDown() {
+ delete mw;
+ delete synth;
+ }
+
+#if 0
+ void _testKitEnable(void)
+ {
+ const char *msg = NULL;
+ mw->transmitMsg("/part0/kit0/Psubenabled", "T");
+ TS_ASSERT(ms->uToB->hasNext());
+ msg = ms->uToB->read();
+ TS_ASSERT_EQUALS(string("/part0/kit0/subpars-data"), msg);
+ TS_ASSERT(ms->uToB->hasNext());
+ msg = ms->uToB->read();
+ TS_ASSERT_EQUALS(string("/part0/kit0/Psubenabled"), msg);
+ }
+
+ void _testBankCapture(void)
+ {
+ mw->transmitMsg("/bank/slots", "");
+ TS_ASSERT(!ms->uToB->hasNext());
+ mw->transmitMsg("/bank/fake", "");
+ TS_ASSERT(ms->uToB->hasNext());
+ const char *msg = ms->uToB->read();
+ TS_ASSERT_EQUALS(string("/bank/fake"), msg);
+ }
+
+ void _testOscCopyPaste(void)
+ {
+ //Enable pad synth
+ mw->transmitMsg("/part0/kit0/Ppadenabled", "T");
+
+ TS_ASSERT(ms->uToB->hasNext());
+ ms->applyOscEvent(ms->uToB->read());
+ TS_ASSERT(ms->uToB->hasNext());
+ ms->applyOscEvent(ms->uToB->read());
+ TS_ASSERT(!ms->uToB->hasNext());
+
+ ms->part[0]->kit[0].adpars->VoicePar[0].FMSmp->Pbasefuncpar = 32;
+
+ int do_exit = 0;
+ std::thread t([&do_exit,this](){
+ int tries = 0;
+ while(tries < 10000 && do_exit == 0) {
+ if(!ms->uToB->hasNext()) {
+ usleep(500);
+ continue;
+ }
+ const char *msg = ms->uToB->read();
+ printf("RT: handling <%s>\n", msg);
+ ms->applyOscEvent(msg);
+ }});
+
+ //Copy From ADsynth modulator
+ printf("====Copy From ADsynth modulator\n");
+ mw->transmitMsg("/presets/copy", "s", "/part0/kit0/adpars/VoicePar0/FMSmp/");
+
+ TS_ASSERT(ms->part[0]->kit[0].padpars->oscilgen->Pbasefuncpar != 32);
+ //Paste to PADsynth
+ printf("====Paste to PADsynth\n");
+ mw->transmitMsg("/presets/paste", "s", "/part0/kit0/padpars/oscilgen/");
+ do_exit = 1;
+ t.join();
+ TS_ASSERT_EQUALS(ms->part[0]->kit[0].padpars->oscilgen->Pbasefuncpar, 32);
+ }
+#endif
+
+ void start_realtime(void)
+ {
+ do_exit = false;
+ realtime = new std::thread([this](){
+ int tries = 0;
+ while(tries < 10000) {
+ if(!ms->uToB->hasNext()) {
+ if(do_exit)
+ break;
+
+ usleep(500);
+ continue;
+ }
+ const char *msg = ms->uToB->read();
+ printf("RT: handling <%s>\n", msg);
+ ms->applyOscEvent(msg);
+ }});
+ }
+
+ void stop_realtime(void)
+ {
+ do_exit = true;
+ realtime->join();
+ delete realtime;
+ realtime = NULL;
+ }
+
+ void run_realtime(void)
+ {
+ start_realtime();
+ stop_realtime();
+ }
+
+ void testMidiLearn(void)
+ {
+ mw->transmitMsg("/learn", "s", "/Pvolume");
+ mw->transmitMsg("/virtual_midi_cc", "iii", 0, 23, 108);
+ TS_ASSERT_EQUALS(ms->Pvolume, 80);
+
+ //Perform a learning operation
+
+ run_realtime(); //1. runs learning and identifies a CC to bind
+ mw->tick(); //2. produces new binding table
+ run_realtime(); //3. applies new binding table
+
+
+ //Verify that the learning actually worked
+ mw->transmitMsg("/virtual_midi_cc", "iii", 0, 23, 13);
+ run_realtime();
+ TS_ASSERT_EQUALS(ms->Pvolume, 13);
+
+ mw->transmitMsg("/virtual_midi_cc", "iii", 0, 23, 2);
+ run_realtime();
+ TS_ASSERT_EQUALS(ms->Pvolume, 2);
+
+ mw->transmitMsg("/virtual_midi_cc", "iii", 0, 23, 0);
+ run_realtime();
+ TS_ASSERT_EQUALS(ms->Pvolume, 0);
+
+ mw->transmitMsg("/virtual_midi_cc", "iii", 0, 23, 127);
+ run_realtime();
+ TS_ASSERT_EQUALS(ms->Pvolume, 127);
+ }
+
+ void testMidiLearnSave(void)
+ {
+ mw->transmitMsg("/learn", "s", "/Pvolume");
+ mw->transmitMsg("/virtual_midi_cc", "iii", 0, 23, 108);
+
+ //Perform a learning operation
+
+ run_realtime(); //1. runs learning and identifies a CC to bind
+ mw->tick(); //2. produces new binding table
+ run_realtime(); //3. applies new binding table
+
+ mw->transmitMsg("/save_xlz", "s", "test-midi-learn.xlz");
+ mw->transmitMsg("/load_xlz", "s", "test-midi-learn.xlz");
+ }
+
+ private:
+ SYNTH_T *synth;
+ MiddleWare *mw;
+ Master *ms;
+ std::thread *realtime;
+ bool do_exit;
+};
diff --git a/src/Tests/MiddlewareTest.h b/src/Tests/MiddlewareTest.h
@@ -39,6 +39,8 @@ using namespace std;
char *instance_name=(char*)"";
+#define NUM_MIDDLEWARE 3
+
class PluginTest:public CxxTest::TestSuite
{
public:
@@ -58,19 +60,19 @@ class PluginTest:public CxxTest::TestSuite
delete synth;
synth = NULL;
- for(int i = 0; i < 16; ++i) {
+ for(int i = 0; i < NUM_MIDDLEWARE; ++i) {
synth = new SYNTH_T;
synth->buffersize = 256;
synth->samplerate = 48000;
//synth->alias();
middleware[i] = new MiddleWare(std::move(*synth), &config);
master[i] = middleware[i]->spawnMaster();
- printf("Octave size = %d\n", master[i]->microtonal.getoctavesize());
+ //printf("Octave size = %d\n", master[i]->microtonal.getoctavesize());
}
}
void tearDown() {
- for(int i = 0; i < 16; ++i)
+ for(int i = 0; i < NUM_MIDDLEWARE; ++i)
delete middleware[i];
delete[] outL;
@@ -82,7 +84,7 @@ class PluginTest:public CxxTest::TestSuite
void testInit() {
for(int x=0; x<100; ++x) {
- for(int i=0; i<16; ++i) {
+ for(int i=0; i<NUM_MIDDLEWARE; ++i) {
middleware[i]->tick();
master[i]->GetAudioOutSamples(rand()%1025,
synth->samplerate, outL, outR);
@@ -102,7 +104,7 @@ class PluginTest:public CxxTest::TestSuite
TS_ASSERT_LESS_THAN(0.1f, sum);
}
-
+
string loadfile(string fname) const
{
std::ifstream t(fname.c_str());
@@ -113,7 +115,7 @@ class PluginTest:public CxxTest::TestSuite
void testLoad(void)
{
- for(int i=0; i<16; ++i) {
+ for(int i=0; i<NUM_MIDDLEWARE; ++i) {
middleware[i]->transmitMsg("/load-part", "is", 0, (string(SOURCE_DIR) + "/../../instruments/banks/Organ/0037-Church Organ 1.xiz").c_str());
middleware[i]->tick();
master[i]->GetAudioOutSamples(synth->buffersize, synth->samplerate, outL, outR);
@@ -133,6 +135,6 @@ class PluginTest:public CxxTest::TestSuite
private:
SYNTH_T *synth;
float *outR, *outL;
- MiddleWare *middleware[16];
- Master *master[16];
+ MiddleWare *middleware[NUM_MIDDLEWARE];
+ Master *master[NUM_MIDDLEWARE];
};
diff --git a/src/UI/BankUI.fl b/src/UI/BankUI.fl
@@ -89,7 +89,7 @@ refreshmainwindow();}
callback {refreshmainwindow();}
xywh {5 8 220 20} down_box BORDER_BOX labelfont 1 align 0 textfont 1 textsize 11
code0 {bankview->init(osc, modeselect, npart);}
- code1 {o->init("loadbank");}
+ code1 {o->init("bank/bank_select");}
class BankList
} {}
Fl_Button {} {
diff --git a/src/UI/BankView.cpp b/src/UI/BankView.cpp
@@ -15,13 +15,14 @@ BankList::BankList(int x,int y, int w, int h, const char *label)
void BankList::init(std::string path)
{
ext = path;
- oscRegister("bank-list");
+ oscRegister("bank/bank_select");
oscRegister(path.c_str());
+ oscWrite("bank/banks", "");
}
void BankList::OSC_raw(const char *msg)
{
- if(!strcmp(msg, "/bank-list") && !strcmp(rtosc_argument_string(msg),"iss")) {
+ if(!strcmp(msg, "/bank/bank_select") && !strcmp(rtosc_argument_string(msg),"iss")) {
const int pos = rtosc_argument(msg, 0).i;
const char *path = rtosc_argument(msg, 1).s;
@@ -31,9 +32,8 @@ void BankList::OSC_raw(const char *msg)
this->clear();
this->add(path);
- osc->write("/loadbank");
}
- if(!strcmp(msg, "/loadbank")&& !strcmp(rtosc_argument_string(msg),"i")) {
+ if(!strcmp(msg, "/bank/bank_select")&& !strcmp(rtosc_argument_string(msg),"i")) {
value(rtosc_argument(msg, 0).i);
}
}
@@ -246,9 +246,12 @@ void BankView::init(Fl_Osc_Interface *osc_, BankViewControls *bvc_, int *npart_)
for(int i=0; i<160; ++i)
slots[i]->init(i, this);
+ //Create Slot Listeners
+ for(int i=0; i<160; ++i)
+ osc->createLink("/bank/slot"+to_s(i), this);
//Request Values
for(int i=0; i<160; ++i)
- osc->write("/refresh_bank", "i", i);
+ osc->write("/bank/slot"+to_s(i), "");
}
/*
@@ -271,8 +274,8 @@ void BankView::react(int event, int nslot)
//Rename slot
if (event==2 && !isempty && mode!=4) {
if(const char *name=fl_input("Slot (instrument) name:", slot.name())) {
- osc->write("/bank-rename", "is", nslot, name);
- osc->write("/refresh_bank", "i", nslot);
+ osc->write("/bank/rename_slot", "is", nslot, name);
+ osc->write("/bank/slot"+to_s(nslot), "");
}
}
@@ -289,8 +292,8 @@ void BankView::react(int event, int nslot)
if(event==1 && mode==2){
if(isempty ||
fl_choice("Overwrite the slot no. %d ?","No","Yes",NULL,nslot+1)) {
- osc->write("/save-bank-part", "ii", *npart, nslot);
- osc->write("/refresh_bank", "i", nslot);
+ osc->write("/bank/save_to_slot", "ii", *npart, nslot);
+ osc->write("/bank/slot"+to_s(nslot), "");
}
bvc->mode(1);
}
@@ -300,8 +303,8 @@ void BankView::react(int event, int nslot)
if(event==1 && mode==3) {
if (!isempty &&
fl_choice("Clear the slot no. %d ?","No","Yes",NULL, nslot+1)) {
- osc->write("/clear-bank-slot", "i", nslot);
- osc->write("/refresh_bank", "i", nslot);
+ osc->write("/bank/clear-slot", "i", nslot);
+ osc->write("/bank/slot"+to_s(nslot), "");
}
bvc->mode(1);
}
@@ -309,9 +312,9 @@ void BankView::react(int event, int nslot)
//Swap
if(mode==4) {
if(event==1 && nselected>=0){
- osc->write("/swap-bank-slots", "ii", nselected, nslot);
- osc->write("/refresh_bank", "i", nslot);
- osc->write("/refresh_bank", "i", nselected);
+ osc->write("/bank/swap_slots", "ii", nselected, nslot);
+ osc->write("/bank/slot"+to_s(nslot), "");
+ osc->write("/bank/slot"+to_s(nselected), "");
nselected=-1;
} else if(nselected<0 || event==2) {
nselected=nslot;
@@ -321,15 +324,22 @@ void BankView::react(int event, int nslot)
void BankView::OSC_raw(const char *msg)
{
- if(strcmp(rtosc_argument_string(msg), "iss"))
- return;
-
- int nslot = rtosc_argument(msg,0).i;
- const char *name = rtosc_argument(msg,1).s;
- const char *fname = rtosc_argument(msg,2).s;
-
- if(0 <= nslot && nslot < 160)
- slots[nslot]->update(name, fname);
+ if(!strcmp(rtosc_argument_string(msg), "iss")) {
+ int nslot = rtosc_argument(msg,0).i;
+ const char *name = rtosc_argument(msg,1).s;
+ const char *fname = rtosc_argument(msg,2).s;
+
+ if(0 <= nslot && nslot < 160)
+ slots[nslot]->update(name, fname);
+ } if(!strcmp(rtosc_argument_string(msg), "ss")) {
+ while(*msg && !isdigit(*msg)) msg++;
+ int nslot = atoi(msg);
+ const char *name = rtosc_argument(msg,0).s;
+ const char *fname = rtosc_argument(msg,1).s;
+
+ if(0 <= nslot && nslot < 160)
+ slots[nslot]->update(name, fname);
+ }
}
void BankView::cbwig(Fl_Widget *w)
@@ -345,6 +355,6 @@ void BankView::refresh(void)
return;
for(int i=0; i<160; ++i)
- osc->write("/refresh_bank", "i", i);
+ osc->write("/bank/slot"+to_s(i), "");
}
diff --git a/src/UI/Connection.cpp b/src/UI/Connection.cpp
@@ -263,7 +263,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->transmitMsg(s.c_str(), args, va);
+ impl->transmitMsg_va(s.c_str(), args, va);
va_end(va);
}
diff --git a/src/UI/MasterUI.fl b/src/UI/MasterUI.fl
@@ -313,6 +313,32 @@ class MasterUI {open
xywh {15 15 100 20} divider
}
MenuItem {} {
+ label {&Load Midi Learn...}
+ callback {char *filename;
+filename=fl_file_chooser("Open:","({*.xlz})",NULL,0);
+if (filename==NULL) return;
+
+osc->write("/load_xlz", "s", filename);}
+ xywh {40 40 100 20}
+ }
+ MenuItem {} {
+ label {Save Midi Learn...}
+ callback {char *filename;
+int result;
+filename=fl_file_chooser("Save:","({*.xlz})",NULL,0);
+if (filename==NULL) return;
+filename=fl_filename_setext(filename,".xlz");
+
+result=fileexists(filename);
+if (result) {
+ result=0;
+ if (!fl_choice("The file exists. \\nOverwrite it?","No","Yes",NULL)) return;
+};
+
+osc->write("/save_xlz", "s", filename);}
+ xywh {30 30 100 20} divider
+ }
+ MenuItem {} {
label {&Load Scale Settings...}
callback {char *filename;
filename=fl_file_chooser("Open:","({*.xsz})",NULL,0);
diff --git a/src/UI/OscilGenUI.fl b/src/UI/OscilGenUI.fl
@@ -121,7 +121,7 @@ class Oscilharmonic {: {public Fl_Group}
display->redraw();}
xywh {0 15 15 115} type {Vert Knob} box NO_BOX selection_color 222 maximum 127 step 1 value 64
- code0 {o->phase=false;//o->value(127-oscil->Phmag[n]);}
+ code0 {o->phase=false;o->ext = "magnitude"+to_s(n);//o->value(127-oscil->Phmag[n]);}
code1 {//if (oscil->Phmag[n]==64) o->selection_color(0);}
class OGSlider
}
@@ -134,7 +134,7 @@ o->osc->requestValue(o->loc+"waveform");
display->redraw();}
xywh {0 135 15 75} type {Vert Knob} box NO_BOX selection_color 222 maximum 127 step 1 value 64
- code0 {o->phase=true;//o->value(oscil->Phphase[n]);}
+ code0 {o->phase=true;o->ext = "phase"+to_s(n);//o->value(oscil->Phphase[n]);}
class OGSlider
}
Fl_Box {} {
diff --git a/src/globals.h b/src/globals.h
@@ -34,7 +34,7 @@
#endif
//Forward Declarations
-namespace rtosc{struct Ports; class ThreadLink;};
+namespace rtosc{struct Ports; struct ClonePorts; struct MergePorts; class ThreadLink;};
class EffectMgr;
class ADnoteParameters;
struct ADnoteGlobalParam;