| @@ -106,6 +106,14 @@ void NotePool::applyLegato(LegatoParams &par) | |||
| } | |||
| }; | |||
| bool NotePool::full(void) const | |||
| { | |||
| for(int i=0; i<POLYPHONY; ++i) | |||
| if(ndesc[i].status == Part::KEY_OFF) | |||
| return false; | |||
| return true; | |||
| } | |||
| //Note that isn't KEY_PLAYING or KEY_RELASED_AND_SUSTAINING | |||
| bool NotePool::existsRunningNote(void) const | |||
| { | |||
| @@ -93,6 +93,8 @@ class NotePool | |||
| void upgradeToLegato(void); | |||
| void applyLegato(LegatoParams &par); | |||
| bool full(void) const; | |||
| //Note that isn't KEY_PLAYING or KEY_RELASED_AND_SUSTAINING | |||
| bool existsRunningNote(void) const; | |||
| int getRunningNotes(void) const; | |||
| @@ -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) | |||
| { | |||
| @@ -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); | |||
| @@ -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; | |||
| @@ -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); | |||
| @@ -398,7 +398,7 @@ void Part::NoteOn(unsigned char note, | |||
| const bool doingLegato = isRunningNote && isLegatoMode() && | |||
| lastlegatomodevalid; | |||
| if(!Pnoteon || !inRange(note, Pminkey, Pmaxkey)) | |||
| if(!Pnoteon || !inRange(note, Pminkey, Pmaxkey) || notePool.full()) | |||
| return; | |||
| verifyKeyMode(); | |||
| @@ -492,7 +492,7 @@ void Part::NoteOff(unsigned char note) //release the key | |||
| monomemPop(note); | |||
| for(auto &desc:notePool.activeDesc()) { | |||
| if(desc.note != note) | |||
| if(desc.note != note || desc.status != KEY_PLAYING) | |||
| continue; | |||
| if(!ctl.sustain.sustain) { //the sustain pedal is not pushed | |||
| if((isMonoMode() || isLegatoMode()) && !monomemEmpty()) | |||
| @@ -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") | |||
| @@ -1,114 +0,0 @@ | |||
| #include <cstdlib> | |||
| #include <cstring> | |||
| #include <string> | |||
| #include <cstdio> | |||
| #include <fstream> | |||
| #include <unistd.h> | |||
| #include <dirent.h> | |||
| #include "Util.h" | |||
| #include "TmpFileMgr.h" | |||
| std::string TmpFileMgr::get_tmp_nam() const | |||
| { | |||
| return tmp_nam_prefix + to_s(getpid()); | |||
| } | |||
| void TmpFileMgr::create_tmp_file(unsigned server_port) const | |||
| { | |||
| std::string tmp_nam = get_tmp_nam(); | |||
| if(0 == access(tmp_nam.c_str(), F_OK)) { | |||
| fprintf(stderr, "Error: Cannot overwrite file %s. " | |||
| "You should probably remove it.", tmp_nam.c_str()); | |||
| exit(EXIT_FAILURE); | |||
| } | |||
| FILE* tmp_fp = fopen(tmp_nam.c_str(), "w"); | |||
| if(!tmp_fp) | |||
| fprintf(stderr, "Warning: could not create new file %s.\n", | |||
| tmp_nam.c_str()); | |||
| else | |||
| fprintf(tmp_fp, "%u", server_port); | |||
| fclose(tmp_fp); | |||
| } | |||
| void TmpFileMgr::clean_up_tmp_nams() const | |||
| { | |||
| DIR *dir; | |||
| struct dirent *entry; | |||
| if ((dir = opendir ("/tmp/")) != nullptr) | |||
| { | |||
| while ((entry = readdir (dir)) != nullptr) | |||
| { | |||
| std::string name = std::string("/tmp/") + entry->d_name; | |||
| if(!name.compare(0, strlen(tmp_nam_prefix),tmp_nam_prefix)) | |||
| { | |||
| std::string pid = name.substr(strlen(tmp_nam_prefix)); | |||
| std::string proc_file = "/proc/" + std::move(pid) + | |||
| "/comm"; | |||
| std::ifstream ifs(proc_file); | |||
| bool remove = false; | |||
| if(!ifs.good()) | |||
| { | |||
| fprintf(stderr, "Note: trying to remove %s - the " | |||
| "process does not exist anymore.\n", | |||
| name.c_str()); | |||
| remove = true; | |||
| } | |||
| else | |||
| { | |||
| std::string comm_name; | |||
| ifs >> comm_name; | |||
| if(comm_name == "zynaddsubfx") | |||
| fprintf(stderr, "Note: detected running " | |||
| "zynaddsubfx with PID %s.\n", | |||
| name.c_str() + strlen(tmp_nam_prefix)); | |||
| else { | |||
| fprintf(stderr, "Note: trying to remove %s - the " | |||
| "PID is owned by\n another " | |||
| "process: %s\n", | |||
| name.c_str(), comm_name.c_str()); | |||
| remove = true; | |||
| } | |||
| } | |||
| if(remove) | |||
| { | |||
| // make sure this file contains only one unsigned | |||
| unsigned udp_port; | |||
| std::ifstream ifs2(name); | |||
| if(!ifs2.good()) | |||
| fprintf(stderr, "Warning: could not open %s.\n", | |||
| name.c_str()); | |||
| else | |||
| { | |||
| ifs2 >> udp_port; | |||
| if(ifs.good()) | |||
| fprintf(stderr, "Warning: could not remove " | |||
| "%s, \n it has not been " | |||
| "written by zynaddsubfx\n", | |||
| name.c_str()); | |||
| else | |||
| { | |||
| if(std::remove(name.c_str()) != 0) | |||
| fprintf(stderr, "Warning: can not remove " | |||
| "%s.\n", name.c_str()); | |||
| } | |||
| } | |||
| } | |||
| /* one might want to connect to zyn here, | |||
| but it is not necessary: | |||
| lo_address la = lo_address_new(nullptr, udp_port.c_str()); | |||
| if(lo_send(la, "/echo", nullptr) <= 0) | |||
| fputs("Note: found crashed file %s\n", stderr); | |||
| lo_address_free(la);*/ | |||
| } | |||
| } | |||
| closedir (dir); | |||
| } else { | |||
| fputs("Warning: can not read /tmp.\n", stderr); | |||
| } | |||
| } | |||
| @@ -1,19 +0,0 @@ | |||
| #pragma once | |||
| /** | |||
| This file provides routines for using zyn's tmp files. | |||
| */ | |||
| class TmpFileMgr | |||
| { | |||
| static constexpr const char* tmp_nam_prefix = "/tmp/zynaddsubfx_"; | |||
| public: | |||
| //! returns file name to where UDP port is saved | |||
| std::string get_tmp_nam() const; | |||
| //! creates a tmp file with given UDP port information | |||
| void create_tmp_file(unsigned server_port) const; | |||
| //! cleans up as many tmp files as possible | |||
| void clean_up_tmp_nams() const; | |||
| }; | |||
| @@ -250,8 +250,12 @@ void XMLwrapper::addpar(const string &name, int val) | |||
| void XMLwrapper::addparreal(const string &name, float val) | |||
| { | |||
| addparams("par_real", 2, "name", name.c_str(), "value", | |||
| stringFrom<float>(val).c_str()); | |||
| union { float in; uint32_t out; } convert; | |||
| char buf[11]; | |||
| convert.in = val; | |||
| sprintf(buf, "0x%8X", convert.out); | |||
| addparams("par_real", 3, "name", name.c_str(), "value", | |||
| stringFrom<float>(val).c_str(), "exact_value", buf); | |||
| } | |||
| void XMLwrapper::addparbool(const string &name, int val) | |||
| @@ -571,7 +575,14 @@ float XMLwrapper::getparreal(const char *name, float defaultpar) const | |||
| if(tmp == NULL) | |||
| return defaultpar; | |||
| const char *strval = mxmlElementGetAttr(tmp, "value"); | |||
| const char *strval = mxmlElementGetAttr(tmp, "exact_value"); | |||
| if (strval != NULL) { | |||
| union { float out; uint32_t in; } convert; | |||
| sscanf(strval+2, "%x", &convert.in); | |||
| return convert.out; | |||
| } | |||
| strval = mxmlElementGetAttr(tmp, "value"); | |||
| if(strval == NULL) | |||
| return defaultpar; | |||
| @@ -621,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; | |||
| } | |||
| @@ -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: | |||
| /** | |||
| @@ -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) | |||
| { | |||
| @@ -223,6 +242,7 @@ static const Ports globalPorts = { | |||
| rParamZyn(PVolume, "volume control"), | |||
| rParamZyn(PAmpVelocityScaleFunction, "Volume Velocity Control"), | |||
| rParamZyn(Fadein_adjustment, "Adjustment for anti-pop strategy."), | |||
| rParamZyn(PPunchStrength, "Punch Strength"), | |||
| rParamZyn(PPunchTime, "UNKNOWN"), | |||
| rParamZyn(PPunchStretch, "How Punch changes with note frequency"), | |||
| @@ -348,6 +368,7 @@ void ADnoteGlobalParam::defaults() | |||
| PAmpVelocityScaleFunction = 64; | |||
| AmpEnvelope->defaults(); | |||
| AmpLfo->defaults(); | |||
| Fadein_adjustment = FADEIN_ADJUSTMENT_SCALE; | |||
| PPunchStrength = 0; | |||
| PPunchTime = 60; | |||
| PPunchStretch = 64; | |||
| @@ -712,6 +733,7 @@ void ADnoteGlobalParam::add2XML(XMLwrapper *xml) | |||
| xml->addpar("volume", PVolume); | |||
| xml->addpar("panning", PPanning); | |||
| xml->addpar("velocity_sensing", PAmpVelocityScaleFunction); | |||
| xml->addpar("fadein_adjustment", Fadein_adjustment); | |||
| xml->addpar("punch_strength", PPunchStrength); | |||
| xml->addpar("punch_time", PPunchTime); | |||
| xml->addpar("punch_stretch", PPunchStretch); | |||
| @@ -788,6 +810,7 @@ void ADnoteGlobalParam::getfromXML(XMLwrapper *xml) | |||
| PAmpVelocityScaleFunction = xml->getpar127("velocity_sensing", | |||
| PAmpVelocityScaleFunction); | |||
| Fadein_adjustment = xml->getpar127("fadein_adjustment", Fadein_adjustment); | |||
| PPunchStrength = xml->getpar127("punch_strength", PPunchStrength); | |||
| PPunchTime = xml->getpar127("punch_time", PPunchTime); | |||
| PPunchStretch = xml->getpar127("punch_stretch", PPunchStretch); | |||
| @@ -982,6 +1005,7 @@ void ADnoteGlobalParam::paste(ADnoteGlobalParam &a) | |||
| copy(PPanning); | |||
| copy(PAmpVelocityScaleFunction); | |||
| copy(Fadein_adjustment); | |||
| copy(PPunchStrength); | |||
| copy(PPunchTime); | |||
| copy(PPunchStretch); | |||
| @@ -30,6 +30,8 @@ enum FMTYPE { | |||
| NONE, MORPH, RING_MOD, PHASE_MOD, FREQ_MOD, PITCH_MOD | |||
| }; | |||
| #define FADEIN_ADJUSTMENT_SCALE 20 | |||
| /*****************************************************************/ | |||
| /* GLOBAL PARAMETERS */ | |||
| /*****************************************************************/ | |||
| @@ -79,6 +81,9 @@ struct ADnoteGlobalParam { | |||
| LFOParams *AmpLfo; | |||
| /* Adjustment factor for anti-pop fadein */ | |||
| unsigned char Fadein_adjustment; | |||
| unsigned char PPunchStrength, PPunchTime, PPunchStretch, | |||
| PPunchVelocitySensing; | |||
| @@ -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_) | |||
| @@ -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, | |||
| @@ -69,6 +69,9 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars) | |||
| pars.GlobalPar. | |||
| PFilterVelocityScaleFunction) - 1); | |||
| NoteGlobalPar.Fadein_adjustment = | |||
| pars.GlobalPar.Fadein_adjustment / (float)FADEIN_ADJUSTMENT_SCALE; | |||
| NoteGlobalPar.Fadein_adjustment *= NoteGlobalPar.Fadein_adjustment; | |||
| if(pars.GlobalPar.PPunchStrength != 0) { | |||
| NoteGlobalPar.Punch.Enabled = 1; | |||
| NoteGlobalPar.Punch.t = 1.0f; //start from 1.0f and to 0.0f | |||
| @@ -1101,6 +1104,7 @@ inline void ADnote::fadein(float *smps) const | |||
| float tmp = (synth.buffersize_f - 1.0f) / (zerocrossings + 1) / 3.0f; | |||
| if(tmp < 8.0f) | |||
| tmp = 8.0f; | |||
| tmp *= NoteGlobalPar.Fadein_adjustment; | |||
| int n; | |||
| F2I(tmp, n); //how many samples is the fade-in | |||
| @@ -144,6 +144,7 @@ class ADnote:public SynthNote | |||
| Envelope *AmpEnvelope; | |||
| LFO *AmpLfo; | |||
| float Fadein_adjustment; | |||
| struct { | |||
| int Enabled; | |||
| float initialvalue, dt, t; | |||
| @@ -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,27 +49,16 @@ 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 " | |||
| "(e.g. rising sawtooth vs a falling sawtooth)"), | |||
| rOption(Pbasefuncmodulation, | |||
| rOptions(None, Rev, Sine, Power), | |||
| rOptions(None, Rev, Sine, Power, Chop), | |||
| "Modulation applied to Base function spectra"), | |||
| rParamZyn(Pbasefuncmodulationpar1, | |||
| "Base function modulation parameter"), | |||
| @@ -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 | |||
| }; | |||
| @@ -486,6 +481,9 @@ void OscilGen::getbasefunction(float *smps) | |||
| case 3: //power | |||
| t += powf((1.0f - cosf((t + p2) * 2.0f * PI)) * 0.5f, p3) * p1; | |||
| break; | |||
| case 4: //chop | |||
| t = t * (powf(2.0, Pbasefuncmodulationpar1/32.0 + | |||
| Pbasefuncmodulationpar2/2048.0)) + p3; | |||
| } | |||
| t = t - floor(t); | |||
| @@ -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, | |||
| @@ -690,6 +690,12 @@ voiceonbutton->redraw();} open | |||
| code0 {o->init("Unison_phase_randomness");} | |||
| class Fl_Osc_Dial | |||
| } | |||
| Fl_Dial {} { | |||
| label Stereo | |||
| tooltip {Stereo Spread} xywh {322 555 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 | |||
| code0 {o->init("Unison_stereo_spread");} | |||
| class Fl_Osc_Dial | |||
| } | |||
| } | |||
| } | |||
| Fl_Group {} { | |||
| @@ -717,7 +723,7 @@ voiceonbutton->redraw();} open | |||
| } {} | |||
| Fl_Dial {} { | |||
| label Pan | |||
| tooltip {Panning (leftmost is Random)} xywh {210 60 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 | |||
| tooltip {Panning (leftmost is Random)} xywh {212 65 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1 | |||
| code0 {o->init("PPanning");} | |||
| class Fl_Osc_Dial | |||
| } | |||
| @@ -856,12 +862,6 @@ bypassfiltercheckbutton->redraw();} | |||
| label {Pink Noise} | |||
| xywh {150 430 300 65} labelfont 1 labelsize 50 labelcolor 212 hide | |||
| } | |||
| Fl_Dial {} { | |||
| label Stereo | |||
| tooltip {Stereo Spread} xywh {327 560 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 | |||
| code0 {o->init("Unison_stereo_spread");} | |||
| class Fl_Osc_Dial | |||
| } | |||
| Fl_Counter {} { | |||
| label Unison selected | |||
| tooltip {Unison size} xywh {20 568 65 18} labelfont 1 align 5 minimum 1 maximum 64 step 1 value 1 textfont 1 textsize 11 | |||
| @@ -1046,27 +1046,34 @@ class ADnoteUI {open : {public PresetsUI_} | |||
| code0 {o->init("PPanning");} | |||
| class Fl_Osc_Dial | |||
| } | |||
| Fl_Dial {} { | |||
| label De-pop selected | |||
| tooltip {Pop suppression} xywh {208 228 20 20} type Float labelsize 10 maximum 127 step 1 textfont 1 textsize 11 | |||
| code0 {o->init("Fadein_adjustment");} | |||
| class Fl_Osc_Dial | |||
| } | |||
| Fl_Dial pstr { | |||
| label {P.Str.} | |||
| tooltip {Punch Strength} xywh {125 237 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 | |||
| tooltip {Punch Strength} xywh {78 237 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 | |||
| code0 {o->init("PPunchStrength");} | |||
| class Fl_Osc_Dial | |||
| } | |||
| Fl_Dial pt { | |||
| label {P.t.} | |||
| tooltip {Punch Time (duration)} xywh {155 237 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 | |||
| tooltip {Punch Time (duration)} xywh {108 237 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 | |||
| code0 {o->init("PPunchTime");} | |||
| class Fl_Osc_Dial | |||
| } | |||
| Fl_Dial pstc { | |||
| label {P.Stc.} | |||
| tooltip {Punch Stretch} xywh {185 237 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 | |||
| tooltip {Punch Stretch} xywh {138 237 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 | |||
| code0 {o->init("PPunchStretch");} | |||
| class Fl_Osc_Dial | |||
| } | |||
| Fl_Dial pvel { | |||
| label {P.Vel.} | |||
| tooltip {Punch Velocity Sensing} xywh {215 237 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 | |||
| tooltip {Punch Velocity Sensing} xywh {168 237 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 | |||
| code0 {o->init("PPunchVelocitySensing");} | |||
| class Fl_Osc_Dial | |||
| } | |||
| @@ -1084,7 +1091,7 @@ class ADnoteUI {open : {public PresetsUI_} | |||
| } {} | |||
| Fl_Check_Button rndgrp { | |||
| label {Rnd Grp} | |||
| tooltip {How the Harmonic Amplitude is applied to voices that use the same oscillator} xywh {70 235 40 25} down_box DOWN_BOX labelsize 10 align 148 | |||
| tooltip {How the Harmonic Amplitude is applied to voices that use the same oscillator} xywh {7 241 65 18} down_box DOWN_BOX labelsize 11 | |||
| code0 {o->init("Hrandgrouping");} | |||
| class Fl_Osc_Check | |||
| } | |||
| @@ -1115,7 +1122,7 @@ class ADnoteUI {open : {public PresetsUI_} | |||
| } | |||
| Fl_Check_Button stereo { | |||
| label Stereo | |||
| xywh {5 230 65 35} down_box DOWN_BOX labelsize 11 | |||
| xywh {7 223 65 18} down_box DOWN_BOX labelsize 11 | |||
| code0 {o->init("PStereo");} | |||
| class Fl_Osc_Check | |||
| } | |||
| @@ -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 {} { | |||
| @@ -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), ""); | |||
| } | |||
| @@ -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); | |||
| } | |||
| @@ -73,7 +73,7 @@ class PointButton {open : {public Fl_Button, public Fl_Osc_Widget}} | |||
| } | |||
| class EnvelopeUI {open : {public Fl_Osc_Group,PresetsUI_} | |||
| } { | |||
| Function {EnvelopeUI(int x,int y, int w, int h, const char *label=0):Fl_Osc_Group(x,y,w,h,label)} {} { | |||
| Function {EnvelopeUI(int x,int y, int w, int h, const char *label=0):Fl_Osc_Group(x,y,w,h)} {} { | |||
| code {freemodeeditwindow=NULL; | |||
| envADSR=NULL; | |||
| envASR=NULL; | |||
| @@ -70,7 +70,7 @@ decl {\#include "FormantFilterGraph.H"} {public local | |||
| class FilterUI {open : {public Fl_Osc_Group,PresetsUI_} | |||
| } { | |||
| Function {FilterUI(int x,int y, int w, int h, const char *label=0):Fl_Osc_Group(x,y,w,h,label)} {} { | |||
| Function {FilterUI(int x,int y, int w, int h, const char *label=0):Fl_Osc_Group(x,y,w,h)} {} { | |||
| code {nvowel=0;nformant=0;nseqpos=0;} {} | |||
| } | |||
| Function {~FilterUI()} {} { | |||
| @@ -107,6 +107,8 @@ int Fl_Osc_Slider::handle(int ev, int X, int Y, int W, int H) | |||
| step = 1; | |||
| } | |||
| int dy = minimum() <= maximum() ? -Fl::e_dy : Fl::e_dy; | |||
| // Flip sense for vertical sliders. | |||
| dy = (this->type() & 1) ? dy : -dy; | |||
| handle_drag(clamp(value() + step * dy)); | |||
| } | |||
| return 1; | |||
| @@ -46,9 +46,9 @@ decl {\#include "common.H"} {public local | |||
| class LFOUI {open : {public Fl_Osc_Group, PresetsUI_} | |||
| } { | |||
| Function {LFOUI(int x,int y, int w, int h, const char *label=0):Fl_Osc_Group(x,y,w,h,label)} {open | |||
| Function {LFOUI(int x,int y, int w, int h, const char *label=0):Fl_Osc_Group(x,y,w,h)} {open | |||
| } { | |||
| code {} {} | |||
| code { cached_label = label; } {} | |||
| } | |||
| Function {~LFOUI()} {open | |||
| } { | |||
| @@ -188,6 +188,8 @@ refresh(); | |||
| lfoui->resize(this->x(),this->y(),this->w(),this->h()); | |||
| lfoparamswindow->label(this->label());} {} | |||
| lfoparamswindow->label(cached_label);} {} | |||
| } | |||
| decl { const char *cached_label; } { private } | |||
| } | |||
| @@ -161,7 +161,12 @@ bankui->show();} | |||
| callback {if ((int)bankui->cbwig->value()!=(npart+1)){ | |||
| bankui->cbwig->value(npart+1); | |||
| bankui->cbwig->do_callback(); | |||
| };} | |||
| } | |||
| if (Fl::event_shift()) | |||
| partui->instrumenteditwindow->show(); | |||
| else if (Fl::event_ctrl()) | |||
| partui->instrumentkitlist->show(); | |||
| } | |||
| xywh {15 235 40 20} labelsize 10 | |||
| } | |||
| Fl_Choice partrcv { | |||
| @@ -196,9 +201,10 @@ o->redraw();} | |||
| code {npart=0; | |||
| bankui=NULL;} {} | |||
| } | |||
| Function {init(int npart_,BankUI *bankui_)} {} { | |||
| Function {init(int npart_,BankUI *bankui_, PartUI *partui_)} {} { | |||
| code {npart=npart_; | |||
| bankui=bankui_; | |||
| partui=partui_; | |||
| ext = "part"+to_s(npart)+"/"; | |||
| make_window(); | |||
| @@ -227,6 +233,8 @@ panellistitemgroup->redraw();} {} | |||
| } | |||
| decl {BankUI *bankui;} {private local | |||
| } | |||
| decl {PartUI *partui;} {private local | |||
| } | |||
| } | |||
| class MasterUI {open | |||
| @@ -304,6 +312,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; | |||
| @@ -903,7 +937,7 @@ GNU General Public License for details.} | |||
| } { | |||
| Fl_Pack {} {open | |||
| xywh {5 10 560 285} type HORIZONTAL | |||
| code0 {for (int i=0;i<NUM_MIDI_PARTS/2;i++){panellistitem[i]=new Panellistitem(0,0,70,260,"");panellistitem[i]->init(i,bankui);}} | |||
| code0 {for (int i=0;i<NUM_MIDI_PARTS/2;i++){panellistitem[i]=new Panellistitem(0,0,70,260,"");panellistitem[i]->init(i,bankui,partui);}} | |||
| } {} | |||
| } | |||
| Fl_Scroll {} {open | |||
| @@ -911,7 +945,7 @@ GNU General Public License for details.} | |||
| } { | |||
| Fl_Pack {} {open | |||
| xywh {5 325 560 285} type HORIZONTAL | |||
| code0 {for (int i=NUM_MIDI_PARTS/2;i<NUM_MIDI_PARTS;i++){panellistitem[i]=new Panellistitem(0,0,70,260,"");panellistitem[i]->init(i,bankui);}} | |||
| code0 {for (int i=NUM_MIDI_PARTS/2;i<NUM_MIDI_PARTS;i++){panellistitem[i]=new Panellistitem(0,0,70,260,"");panellistitem[i]->init(i,bankui,partui);}} | |||
| } {} | |||
| } | |||
| Fl_Button {} { | |||
| @@ -1681,6 +1715,7 @@ char *tmp; | |||
| if (result<0) fl_alert("Error: Could not save the file."); | |||
| else | |||
| { | |||
| osc->write("/last_xmz", "s", filename); | |||
| \#if USE_NSM | |||
| if ( nsm && nsm->is_active() ) | |||
| setfilelabel( nsm->display_name ); | |||
| @@ -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 {} { | |||
| @@ -252,7 +252,7 @@ class OscilEditor {open : {public PresetsUI_} | |||
| label rnd | |||
| callback {} | |||
| tooltip {Oscilator Phase Randomness: smaller than 0 is "group", larger than 0 is for each harmonic} xywh {145 290 100 10} type {Horz Knob} box NO_BOX labelsize 10 align 5 minimum -64 maximum 63 step 1 | |||
| code0 {(void)o->init("Prand");//if (oscil->ADvsPAD) o->hide();} | |||
| code0 {(void)o->init("Prand"); if (!adnotep) o->hide();} | |||
| class Fl_Osc_VSlider | |||
| } | |||
| Fl_Group {} {open | |||
| @@ -454,6 +454,10 @@ redrawoscil();} | |||
| label Pow | |||
| xywh {85 85 100 20} labelfont 1 labelsize 10 | |||
| } | |||
| MenuItem {} { | |||
| label Chop | |||
| xywh {95 95 100 20} labelfont 1 labelsize 10 | |||
| } | |||
| } | |||
| Fl_Dial bfmodpar1 { | |||
| callback {redrawoscil();} | |||
| @@ -137,11 +137,11 @@ maxkcounter->do_callback();} | |||
| Fl_Button adeditbutton { | |||
| label edit | |||
| callback { | |||
| if (Fl::event_shift()) { | |||
| if (Fl::event_shift()) | |||
| partui->showvoiceparams(n, true); | |||
| } else if (Fl::event_ctrl()) { | |||
| else if (Fl::event_ctrl()) | |||
| partui->showvoiceparams(n, false); | |||
| } else | |||
| else | |||
| partui->showparameters(n,0); | |||
| } | |||
| xywh {420 0 40 15} box THIN_UP_BOX labelsize 11 | |||
| @@ -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; | |||
| @@ -139,20 +139,42 @@ void MidiMappernRT::map(const char *addr, bool coarse) | |||
| unMap(addr, coarse); | |||
| learnQueue.push_back(std::make_pair(addr,coarse)); | |||
| char buf[1024]; | |||
| rtosc_message(buf, 1024, "/midi-add-watch",""); | |||
| rtosc_message(buf, 1024, "/midi-learn/midi-add-watch",""); | |||
| rt_cb(buf); | |||
| } | |||
| MidiMapperStorage *MidiMappernRT::generateNewBijection(const Port &port, std::string addr) | |||
| { | |||
| MidiBijection bi; | |||
| const auto &meta = port.meta(); | |||
| if(meta.find("min") == meta.end() || | |||
| meta.find("max") == meta.end()) { | |||
| printf("Rtosc-MIDI: Cannot Learn address = <%s>\n", addr.c_str()); | |||
| printf("Rtosc-MIDI: There are no min/max fields\n"); | |||
| return NULL; | |||
| } | |||
| bi.mode = 0; | |||
| bi.min = atof(port.meta()["min"]); | |||
| bi.max = atof(port.meta()["max"]); | |||
| auto tmp = [bi,addr](int16_t x, MidiMapperStorage::write_cb cb) { | |||
| char type = 'f'; | |||
| if(strstr(port.name, ":i")) | |||
| type = 'i'; | |||
| std::function<void(int16_t, MidiMapperStorage::write_cb cb)> tmp = | |||
| [bi,addr,type](int16_t x, MidiMapperStorage::write_cb cb) { | |||
| float out = bi(x); | |||
| //printf("in = %d out = %f\n", x, out); | |||
| char buf[1024]; | |||
| rtosc_message(buf, 1024, addr.c_str(), "f", out); | |||
| if(type == 'f') | |||
| rtosc_message(buf, 1024, addr.c_str(), "f", out); | |||
| else | |||
| rtosc_message(buf, 1024, addr.c_str(), "i", (int)out); | |||
| cb(buf); | |||
| }; | |||
| if(bi.min == 0 && bi.max == 127 && type =='i') | |||
| tmp = [bi,addr,type](int16_t x, MidiMapperStorage::write_cb cb) { | |||
| //printf("special case in = %x out = %d\n", x, 0x7f&(x>>7)); | |||
| char buf[1024]; | |||
| rtosc_message(buf, 1024, addr.c_str(), "i", 0x7f&(x>>7)); | |||
| cb(buf); | |||
| }; | |||
| @@ -177,10 +199,19 @@ void MidiMappernRT::addNewMapper(int ID, const Port &port, std::string addr) | |||
| bi.mode = 0; | |||
| bi.min = atof(port.meta()["min"]); | |||
| bi.max = atof(port.meta()["max"]); | |||
| auto tmp = [bi,addr](int16_t x, MidiMapperStorage::write_cb cb) { | |||
| char type = 'f'; | |||
| if(strstr(port.name, ":i")) | |||
| type = 'i'; | |||
| //printf("ADDING TYPE %c\n", type); | |||
| auto tmp = [bi,addr,type](int16_t x, MidiMapperStorage::write_cb cb) { | |||
| float out = bi(x); | |||
| //printf("in = %d out = %f\n", x, out); | |||
| char buf[1024]; | |||
| rtosc_message(buf, 1024, addr.c_str(), "f", out); | |||
| if(type == 'f') | |||
| rtosc_message(buf, 1024, addr.c_str(), "f", out); | |||
| else | |||
| rtosc_message(buf, 1024, addr.c_str(), "i", (int)out); | |||
| cb(buf); | |||
| }; | |||
| @@ -197,6 +228,10 @@ void MidiMappernRT::addNewMapper(int ID, const Port &port, std::string addr) | |||
| } | |||
| storage = nstorage; | |||
| inv_map[addr] = std::make_tuple(storage->callbacks.size()-1, ID,-1,bi); | |||
| char buf[1024]; | |||
| rtosc_message(buf, 1024, "/midi-learn/midi-bind", "b", sizeof(storage), &storage); | |||
| rt_cb(buf); | |||
| } | |||
| void MidiMappernRT::addFineMapper(int ID, const Port &port, std::string addr) | |||
| @@ -234,7 +269,7 @@ void MidiMappernRT::useFreeID(int ID) | |||
| return; | |||
| std::string addr = std::get<0>(learnQueue.front()); | |||
| bool coarse = std::get<1>(learnQueue.front()); | |||
| learnQueue.pop_front(); | |||
| assert(base_ports); | |||
| const rtosc::Port *p = base_ports->apropos(addr.c_str()); | |||
| @@ -264,13 +299,13 @@ void MidiMappernRT::useFreeID(int ID) | |||
| //TODO clean up unused value and callback objects | |||
| char buf[1024]; | |||
| rtosc_message(buf, 1024, "/midi-bind", "b", sizeof(storage), &storage); | |||
| rtosc_message(buf, 1024, "/midi-learn/midi-bind", "b", sizeof(storage), &storage); | |||
| rt_cb(buf); | |||
| }; | |||
| void MidiMappernRT::unMap(const char *addr, bool coarse) | |||
| { | |||
| printf("Unmapping('%s',%d)\n",addr,coarse); | |||
| //printf("Unmapping('%s',%d)\n",addr,coarse); | |||
| if(inv_map.find(addr) == inv_map.end()) | |||
| return; | |||
| auto imap = inv_map[addr]; | |||
| @@ -296,7 +331,7 @@ void MidiMappernRT::unMap(const char *addr, bool coarse) | |||
| //TODO clean up unused value and callback objects | |||
| char buf[1024]; | |||
| rtosc_message(buf, 1024, "/midi-bind", "b", sizeof(storage), &storage); | |||
| rtosc_message(buf, 1024, "/midi-learn/midi-bind", "b", sizeof(storage), &storage); | |||
| rt_cb(buf); | |||
| } | |||
| @@ -386,7 +421,7 @@ void MidiMappernRT::apply_low(int v, int ID) { apply_midi(0x7f&v,ID);} | |||
| void MidiMappernRT::apply_midi(int val, int ID) | |||
| { | |||
| char buf[1024]; | |||
| rtosc_message(buf,1024,"/virtual_midi_cc","ii",val,ID); | |||
| rtosc_message(buf,1024,"/virtual_midi_cc","iii",0,val,ID); | |||
| rt_cb(buf); | |||
| } | |||
| @@ -408,9 +443,9 @@ void MidiMappernRT::setBounds(const char *str, float low, float high) | |||
| }; | |||
| storage = nstorage; | |||
| char buf[1024]; | |||
| rtosc_message(buf, 1024, "/midi-bind", "b", sizeof(storage), &storage); | |||
| rtosc_message(buf, 1024, "/midi-learn/midi-bind", "b", sizeof(storage), &storage); | |||
| rt_cb(buf); | |||
| } | |||
| @@ -497,7 +532,8 @@ MidiMapperRT::MidiMapperRT(void) | |||
| {} | |||
| void MidiMapperRT::setBackendCb(std::function<void(const char*)> cb) {backend = cb;} | |||
| void MidiMapperRT::setFrontendCb(std::function<void(const char*)> cb) {frontend = cb;} | |||
| void MidiMapperRT::handleCC(int ID, int val) { | |||
| void MidiMapperRT::handleCC(int ID, int val) { | |||
| //printf("handling CC(%d,%d){%d,%d,%d}\n", ID, val, (int)storage, pending.has(ID), watchSize); | |||
| if((!storage || !storage->handleCC(ID, val, backend)) && !pending.has(ID) && watchSize) { | |||
| watchSize--; | |||
| pending.insert(ID); | |||
| @@ -508,6 +544,30 @@ void MidiMapperRT::handleCC(int ID, int val) { | |||
| } | |||
| void MidiMapperRT::addWatch(void) {watchSize++;} | |||
| void MidiMapperRT::remWatch(void) {if(watchSize) watchSize--;} | |||
| const rtosc::Ports MidiMapperRT::ports = { | |||
| {"midi-add-watch",0,0, [](msg_t, RtData&d) | |||
| { | |||
| auto midi = (MidiMapperRT*)d.obj; | |||
| midi->addWatch();}}, | |||
| {"midi-remove-watch",0,0, [](msg_t, RtData&d) | |||
| { | |||
| auto midi = (MidiMapperRT*)d.obj; | |||
| midi->remWatch();}}, | |||
| {"midi-bind:b","",0, [](msg_t msg, RtData&d) | |||
| { | |||
| auto &midi = *(MidiMapperRT*)d.obj; | |||
| midi.pending.pop(); | |||
| MidiMapperStorage *nstorage = | |||
| *(MidiMapperStorage**)rtosc_argument(msg,0).b.data; | |||
| if(midi.storage) { | |||
| nstorage->cloneValues(*midi.storage); | |||
| midi.storage = nstorage; | |||
| } else | |||
| midi.storage = nstorage;}} | |||
| }; | |||
| //Depricated | |||
| Port MidiMapperRT::addWatchPort(void) { | |||
| return Port{"midi-add-watch","",0, [this](msg_t, RtData&) { | |||
| this->addWatch(); | |||
| @@ -16,7 +16,7 @@ static inline void scat(char *dest, const char *src) | |||
| } | |||
| RtData::RtData(void) | |||
| :loc(NULL), loc_size(0), obj(NULL), matches(0) | |||
| :loc(NULL), loc_size(0), obj(NULL), matches(0), message(NULL) | |||
| {} | |||
| void RtData::reply(const char *path, const char *args, ...) | |||
| @@ -42,6 +42,9 @@ void RtData::broadcast(const char *path, const char *args, ...) | |||
| void RtData::broadcast(const char *msg) | |||
| {reply(msg);}; | |||
| void RtData::forward(const char *rational) | |||
| {} | |||
| void metaiterator_advance(const char *&title, const char *&value) | |||
| { | |||
| if(!title || !*title) { | |||
| @@ -373,6 +376,8 @@ static ivec_t find_remap(words_t &strs, ivec_t &pos, ivec_t &assoc) | |||
| static void generate_minimal_hash(std::vector<std::string> str, Port_Matcher &pm) | |||
| { | |||
| if(str.empty()) | |||
| return; | |||
| pm.pos = find_pos(str); | |||
| if(pm.pos.empty()) { | |||
| fprintf(stderr, "rtosc: Failed to generate minimal hash\n"); | |||
| @@ -413,14 +418,9 @@ static void generate_minimal_hash(Ports &p, Port_Matcher &pm) | |||
| } | |||
| Ports::Ports(std::initializer_list<Port> l) | |||
| :ports(l), impl(new Port_Matcher) | |||
| :ports(l), impl(NULL) | |||
| { | |||
| generate_minimal_hash(*this, *impl); | |||
| impl->enump = new bool[ports.size()]; | |||
| for(int i=0; i<(int)ports.size(); ++i) | |||
| impl->enump[i] = strchr(ports[i].name, '#'); | |||
| elms = ports.size(); | |||
| refreshMagic(); | |||
| } | |||
| Ports::~Ports() | |||
| @@ -433,9 +433,20 @@ Ports::~Ports() | |||
| #define __builtin_expect(a,b) a | |||
| #endif | |||
| void Ports::dispatch(const char *m, rtosc::RtData &d) const | |||
| void Ports::dispatch(const char *m, rtosc::RtData &d, bool base_dispatch) const | |||
| { | |||
| void *obj = d.obj; | |||
| //handle the first dispatch layer | |||
| if(base_dispatch) { | |||
| d.matches = 0; | |||
| d.message = m; | |||
| if(m && *m == '/') | |||
| m++; | |||
| if(d.loc) | |||
| d.loc[0] = 0; | |||
| } | |||
| //simple case | |||
| if(!d.loc || !d.loc_size) { | |||
| for(const Port &port: ports) { | |||
| @@ -501,8 +512,14 @@ void Ports::dispatch(const char *m, rtosc::RtData &d) const | |||
| for(auto p:impl->pos) | |||
| if(p < (int)len) | |||
| t += impl->assoc[m[p]]; | |||
| if(t >= (int)impl->remap.size()) | |||
| if(t >= (int)impl->remap.size() && !default_handler) | |||
| return; | |||
| else if(t >= (int)impl->remap.size() && default_handler) { | |||
| d.matches++; | |||
| default_handler(m,d), d.obj = obj; | |||
| return; | |||
| } | |||
| int port_num = impl->remap[t]; | |||
| //Verify the chosen port is correct | |||
| @@ -531,6 +548,9 @@ void Ports::dispatch(const char *m, rtosc::RtData &d) const | |||
| //Remove the rest of the path | |||
| old_end[0] = '\0'; | |||
| } else if(default_handler) { | |||
| d.matches++; | |||
| default_handler(m,d), d.obj = obj; | |||
| } | |||
| } | |||
| } | |||
| @@ -640,6 +660,63 @@ char *Ports::collapsePath(char *p) | |||
| return write_pos+1; | |||
| }; | |||
| void Ports::refreshMagic() | |||
| { | |||
| delete impl; | |||
| impl = new Port_Matcher; | |||
| generate_minimal_hash(*this, *impl); | |||
| impl->enump = new bool[ports.size()]; | |||
| for(int i=0; i<(int)ports.size(); ++i) | |||
| impl->enump[i] = strchr(ports[i].name, '#'); | |||
| elms = ports.size(); | |||
| } | |||
| ClonePorts::ClonePorts(const Ports &ports_, | |||
| std::initializer_list<ClonePort> c) | |||
| :Ports({}) | |||
| { | |||
| for(auto &to_clone:c) { | |||
| const Port *clone_port = NULL; | |||
| for(auto &p:ports_.ports) | |||
| if(!strcmp(p.name, to_clone.name)) | |||
| clone_port = &p; | |||
| if(!clone_port && strcmp("*", to_clone.name)) { | |||
| fprintf(stderr, "Cannot find a clone port for '%s'\n",to_clone.name); | |||
| assert(false); | |||
| } | |||
| if(clone_port) { | |||
| ports.push_back({clone_port->name, clone_port->metadata, | |||
| clone_port->ports, to_clone.cb}); | |||
| } else { | |||
| default_handler = to_clone.cb; | |||
| } | |||
| } | |||
| refreshMagic(); | |||
| } | |||
| MergePorts::MergePorts(std::initializer_list<const rtosc::Ports*> c) | |||
| :Ports({}) | |||
| { | |||
| //XXX TODO remove duplicates in some sane and documented way | |||
| //e.g. repeated ports override and remove older ones | |||
| for(auto *to_clone:c) { | |||
| assert(to_clone); | |||
| for(auto &p:to_clone->ports) { | |||
| bool already_there = false; | |||
| for(auto &pp:ports) | |||
| if(!strcmp(pp.name, p.name)) | |||
| already_there = true; | |||
| if(!already_there) | |||
| ports.push_back(p); | |||
| } | |||
| } | |||
| refreshMagic(); | |||
| } | |||
| void rtosc::walk_ports(const Ports *base, | |||
| char *name_buffer, | |||
| size_t buffer_size, | |||
| @@ -26,15 +26,70 @@ static bool rtosc_match_number(const char **pattern, const char **msg) | |||
| return val < max; | |||
| } | |||
| // pattern = /previous/{A,B,C,D,E}/after | |||
| // ^ | |||
| // message = /previous/C/after | |||
| // ^ | |||
| const char *rtosc_match_options(const char *pattern, const char **msg) | |||
| { | |||
| const char *preserve = *msg; | |||
| assert(*pattern == '{'); | |||
| pattern++; | |||
| retry: | |||
| while(1) { | |||
| //Check for special characters | |||
| if(*pattern == ',' || *pattern == '}') { | |||
| goto advance_until_end; | |||
| } else if((*pattern == **msg)) { //verbatim compare | |||
| if(**msg) | |||
| ++pattern, ++*msg; | |||
| else | |||
| goto try_next; | |||
| } else | |||
| goto try_next; | |||
| } | |||
| advance_until_end: | |||
| while(*pattern && *pattern != '}') pattern++; | |||
| if(*pattern == '}') | |||
| pattern++; | |||
| return pattern; | |||
| try_next: | |||
| *msg = preserve; | |||
| while(*pattern && *pattern != '}' && *pattern != ',') pattern++; | |||
| if(*pattern == ',') { | |||
| pattern++; | |||
| goto retry; | |||
| } | |||
| return NULL; | |||
| } | |||
| const char *rtosc_match_path(const char *pattern, const char *msg) | |||
| { | |||
| while(1) { | |||
| //Check for special characters | |||
| if(*pattern == ':' && !*msg) | |||
| return pattern; | |||
| else if(*pattern == '/' && *msg == '/') | |||
| return ++pattern; | |||
| else if(*pattern == '#') { | |||
| else if(*pattern == '{') { | |||
| pattern = rtosc_match_options(pattern, &msg); | |||
| if(!pattern) | |||
| return NULL; | |||
| } else if(*pattern == '*') { | |||
| //advance message and pattern to '/' or ':' and '\0' | |||
| while(*pattern && *pattern != '/' && *pattern != ':') | |||
| pattern++; | |||
| if(*pattern == '/' || *pattern == ':') | |||
| while(*msg && *msg != '/') | |||
| msg++; | |||
| } else if(*pattern == '/' && *msg == '/') { | |||
| ++pattern; | |||
| ++msg; | |||
| if(*pattern == '\0' || *pattern == ':') | |||
| return pattern; | |||
| } else if(*pattern == '#') { | |||
| ++pattern; | |||
| if(!rtosc_match_number(&pattern, &msg)) | |||
| return NULL; | |||
| @@ -121,7 +121,7 @@ class MidiMappernRT | |||
| std::string getMappedString(std::string addr); | |||
| MidiBijection getBijection(std::string s); | |||
| void snoop(const char *msg); | |||
| void apply_high(int v, int ID); | |||
| @@ -158,10 +158,14 @@ class MidiMapperRT | |||
| void handleCC(int ID, int val); | |||
| void addWatch(void); | |||
| void remWatch(void); | |||
| //Depricated | |||
| Port addWatchPort(void); | |||
| Port removeWatchPort(void); | |||
| Port bindPort(void); | |||
| static const Ports ports; | |||
| //Fixed upper bounded size set of integer IDs | |||
| class PendingQueue | |||
| { | |||
| @@ -52,11 +52,16 @@ struct RtData | |||
| void *obj; | |||
| int matches; | |||
| const Port *port; | |||
| const char *message; | |||
| virtual void reply(const char *path, const char *args, ...); | |||
| virtual void reply(const char *msg); | |||
| virtual void chain(const char *path, const char *args, ...){}; | |||
| virtual void chain(const char *msg){}; | |||
| virtual void broadcast(const char *path, const char *args, ...); | |||
| virtual void broadcast(const char *msg); | |||
| virtual void forward(const char *rational=NULL); | |||
| }; | |||
| @@ -123,7 +128,7 @@ struct Port { | |||
| struct Ports | |||
| { | |||
| std::vector<Port> ports; | |||
| std::function<void(msg_t, RtData&)> default_handler; | |||
| typedef std::vector<Port>::const_iterator itr_t; | |||
| @@ -152,7 +157,7 @@ struct Ports | |||
| * @param d The RtData object shall contain a path buffer (or null), the length of | |||
| * the buffer, a pointer to data. | |||
| */ | |||
| void dispatch(const char *m, RtData &d) const; | |||
| void dispatch(const char *m, RtData &d, bool base_dispatch=false) const; | |||
| /** | |||
| * Retrieve local port by name | |||
| @@ -187,12 +192,31 @@ struct Ports | |||
| */ | |||
| static char *collapsePath(char *p); | |||
| protected: | |||
| void refreshMagic(void); | |||
| private: | |||
| //Performance hacks | |||
| class Port_Matcher *impl; | |||
| unsigned elms; | |||
| }; | |||
| struct ClonePort | |||
| { | |||
| const char *name; | |||
| std::function<void(msg_t, RtData&)> cb; | |||
| }; | |||
| struct ClonePorts:public Ports | |||
| { | |||
| ClonePorts(const Ports &p, | |||
| std::initializer_list<ClonePort> c); | |||
| }; | |||
| struct MergePorts:public Ports | |||
| { | |||
| MergePorts(std::initializer_list<const Ports*> c); | |||
| }; | |||
| /********************* | |||
| * Port walking code * | |||