Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1477 lines
46KB

  1. #include "MiddleWare.h"
  2. #include <cstring>
  3. #include <cstdio>
  4. #include <cstdlib>
  5. #include <fstream>
  6. #include <rtosc/undo-history.h>
  7. #include <rtosc/thread-link.h>
  8. #include <rtosc/ports.h>
  9. #include <rtosc/typed-message.h>
  10. #include <lo/lo.h>
  11. #include <unistd.h>
  12. #include "../UI/Connection.h"
  13. #include "../UI/Fl_Osc_Interface.h"
  14. #include <map>
  15. #include "Util.h"
  16. #include "Master.h"
  17. #include "Part.h"
  18. #include "PresetExtractor.h"
  19. #include "../Params/PresetsStore.h"
  20. #include "../Params/ADnoteParameters.h"
  21. #include "../Params/SUBnoteParameters.h"
  22. #include "../Params/PADnoteParameters.h"
  23. #include "../DSP/FFTwrapper.h"
  24. #include "../Synth/OscilGen.h"
  25. #include "../Nio/Nio.h"
  26. #include <string>
  27. #include <future>
  28. #include <atomic>
  29. #include <list>
  30. #ifndef errx
  31. #include <err.h>
  32. #endif
  33. using std::string;
  34. /******************************************************************************
  35. * LIBLO And Reflection Code *
  36. * *
  37. * All messages that are handled are handled in a serial fashion. *
  38. * Thus, changes in the current interface sending messages can be encoded *
  39. * into the stream via events which simply echo back the active interface *
  40. ******************************************************************************/
  41. static void liblo_error_cb(int i, const char *m, const char *loc)
  42. {
  43. fprintf(stderr, "liblo :-( %d-%s@%s\n",i,m,loc);
  44. }
  45. void path_search(const char *m, const char *url)
  46. {
  47. using rtosc::Ports;
  48. using rtosc::Port;
  49. //assumed upper bound of 32 ports (may need to be resized)
  50. char types[129];
  51. rtosc_arg_t args[128];
  52. size_t pos = 0;
  53. const Ports *ports = NULL;
  54. const char *str = rtosc_argument(m,0).s;
  55. const char *needle = rtosc_argument(m,1).s;
  56. //zero out data
  57. memset(types, 0, sizeof(types));
  58. memset(args, 0, sizeof(args));
  59. if(!*str) {
  60. ports = &Master::ports;
  61. } else {
  62. const Port *port = Master::ports.apropos(rtosc_argument(m,0).s);
  63. if(port)
  64. ports = port->ports;
  65. }
  66. if(ports) {
  67. //RTness not confirmed here
  68. for(const Port &p:*ports) {
  69. if(strstr(p.name, needle)!=p.name)
  70. continue;
  71. types[pos] = 's';
  72. args[pos++].s = p.name;
  73. types[pos] = 'b';
  74. if(p.metadata && *p.metadata) {
  75. args[pos].b.data = (unsigned char*) p.metadata;
  76. auto tmp = rtosc::Port::MetaContainer(p.metadata);
  77. args[pos++].b.len = tmp.length();
  78. } else {
  79. args[pos].b.data = (unsigned char*) NULL;
  80. args[pos++].b.len = 0;
  81. }
  82. }
  83. }
  84. //Reply to requester [wow, these messages are getting huge...]
  85. char buffer[1024*20];
  86. size_t length = rtosc_amessage(buffer, sizeof(buffer), "/paths", types, args);
  87. if(length) {
  88. lo_message msg = lo_message_deserialise((void*)buffer, length, NULL);
  89. lo_address addr = lo_address_new_from_url(url);
  90. if(addr)
  91. lo_send_message(addr, buffer, msg);
  92. }
  93. }
  94. static int handler_function(const char *path, const char *types, lo_arg **argv,
  95. int argc, lo_message msg, void *user_data)
  96. {
  97. (void) types;
  98. (void) argv;
  99. (void) argc;
  100. MiddleWare *mw = (MiddleWare*)user_data;
  101. lo_address addr = lo_message_get_source(msg);
  102. if(addr) {
  103. const char *tmp = lo_address_get_url(addr);
  104. if(tmp != mw->activeUrl()) {
  105. mw->transmitMsg("/echo", "ss", "OSC_URL", tmp);
  106. mw->activeUrl(tmp);
  107. }
  108. }
  109. char buffer[2048];
  110. memset(buffer, 0, sizeof(buffer));
  111. size_t size = 2048;
  112. lo_message_serialise(msg, path, buffer, &size);
  113. if(!strcmp(buffer, "/path-search") && !strcmp("ss", rtosc_argument_string(buffer))) {
  114. path_search(buffer, mw->activeUrl().c_str());
  115. } else if(buffer[0]=='/' && rindex(buffer, '/')[1]) {
  116. mw->transmitMsg(rtosc::Ports::collapsePath(buffer));
  117. }
  118. return 0;
  119. }
  120. typedef void(*cb_t)(void*,const char*);
  121. /*****************************************************************************
  122. * Memory Deallocation *
  123. *****************************************************************************/
  124. void deallocate(const char *str, void *v)
  125. {
  126. //printf("deallocating a '%s' at '%p'\n", str, v);
  127. if(!strcmp(str, "Part"))
  128. delete (Part*)v;
  129. else if(!strcmp(str, "Master"))
  130. delete (Master*)v;
  131. else if(!strcmp(str, "fft_t"))
  132. delete[] (fft_t*)v;
  133. else
  134. fprintf(stderr, "Unknown type '%s', leaking pointer %p!!\n", str, v);
  135. }
  136. /*****************************************************************************
  137. * PadSynth Setup *
  138. *****************************************************************************/
  139. void preparePadSynth(string path, PADnoteParameters *p, rtosc::ThreadLink *uToB)
  140. {
  141. //printf("preparing padsynth parameters\n");
  142. assert(!path.empty());
  143. path += "sample";
  144. unsigned max = 0;
  145. p->sampleGenerator([&max,&path,uToB]
  146. (unsigned N, PADnoteParameters::Sample &s)
  147. {
  148. max = max<N ? N : max;
  149. //printf("sending info to '%s'\n", (path+to_s(N)).c_str());
  150. uToB->write((path+to_s(N)).c_str(), "ifb",
  151. s.size, s.basefreq, sizeof(float*), &s.smp);
  152. }, []{return false;});
  153. //clear out unused samples
  154. for(unsigned i = max+1; i < PAD_MAX_SAMPLES; ++i) {
  155. uToB->write((path+to_s(i)).c_str(), "ifb",
  156. 0, 440.0f, sizeof(float*), NULL);
  157. }
  158. }
  159. /******************************************************************************
  160. * MIDI Serialization *
  161. * *
  162. ******************************************************************************/
  163. void saveMidiLearn(XMLwrapper &xml, const rtosc::MidiMappernRT &midi)
  164. {
  165. xml.beginbranch("midi-learn");
  166. for(auto value:midi.inv_map) {
  167. XmlNode binding("midi-binding");
  168. auto biject = std::get<3>(value.second);
  169. binding["osc-path"] = value.first;
  170. binding["coarse-CC"] = to_s(std::get<1>(value.second));
  171. binding["fine-CC"] = to_s(std::get<2>(value.second));
  172. binding["type"] = "i";
  173. binding["minimum"] = to_s(biject.min);
  174. binding["maximum"] = to_s(biject.max);
  175. xml.add(binding);
  176. }
  177. xml.endbranch();
  178. }
  179. void loadMidiLearn(XMLwrapper &xml, rtosc::MidiMappernRT &midi)
  180. {
  181. using rtosc::Port;
  182. if(xml.enterbranch("midi-learn")) {
  183. auto nodes = xml.getBranch();
  184. //TODO clear mapper
  185. for(auto node:nodes) {
  186. if(node.name != "midi-binding" ||
  187. !node.has("osc-path") ||
  188. !node.has("coarse-CC"))
  189. continue;
  190. const string path = node["osc-path"];
  191. const int CC = atoi(node["coarse-CC"].c_str());
  192. const Port *p = Master::ports.apropos(path.c_str());
  193. if(p) {
  194. printf("loading midi port...\n");
  195. midi.addNewMapper(CC, *p, path);
  196. } else {
  197. printf("unknown midi bindable <%s>\n", path.c_str());
  198. }
  199. }
  200. xml.exitbranch();
  201. } else
  202. printf("cannot find 'midi-learn' branch...\n");
  203. }
  204. /******************************************************************************
  205. * Non-RealTime Object Store *
  206. * *
  207. * *
  208. * Storage For Objects which need to be interfaced with outside the realtime *
  209. * thread (aka they have long lived operations which can be done out-of-band) *
  210. * *
  211. * - OscilGen instances as prepare() cannot be done in realtime and PAD *
  212. * depends on these instances *
  213. * - PADnoteParameter instances as applyparameters() cannot be done in *
  214. * realtime *
  215. * *
  216. * These instances are collected on every part change and kit change *
  217. ******************************************************************************/
  218. struct NonRtObjStore
  219. {
  220. std::map<std::string, void*> objmap;
  221. void extractMaster(Master *master)
  222. {
  223. for(int i=0; i < NUM_MIDI_PARTS; ++i) {
  224. extractPart(master->part[i], i);
  225. }
  226. }
  227. void extractPart(Part *part, int i)
  228. {
  229. for(int j=0; j < NUM_KIT_ITEMS; ++j) {
  230. auto &obj = part->kit[j];
  231. extractAD(obj.adpars, i, j);
  232. extractPAD(obj.padpars, i, j);
  233. }
  234. }
  235. void extractAD(ADnoteParameters *adpars, int i, int j)
  236. {
  237. std::string base = "/part"+to_s(i)+"/kit"+to_s(j)+"/";
  238. for(int k=0; k<NUM_VOICES; ++k) {
  239. std::string nbase = base+"adpars/VoicePar"+to_s(k)+"/";
  240. if(adpars) {
  241. auto &nobj = adpars->VoicePar[k];
  242. objmap[nbase+"OscilSmp/"] = nobj.OscilSmp;
  243. objmap[nbase+"FMSmp/"] = nobj.FMSmp;
  244. } else {
  245. objmap[nbase+"OscilSmp/"] = nullptr;
  246. objmap[nbase+"FMSmp/"] = nullptr;
  247. }
  248. }
  249. }
  250. void extractPAD(PADnoteParameters *padpars, int i, int j)
  251. {
  252. std::string base = "/part"+to_s(i)+"/kit"+to_s(j)+"/";
  253. for(int k=0; k<NUM_VOICES; ++k) {
  254. if(padpars) {
  255. objmap[base+"padpars/"] = padpars;
  256. objmap[base+"padpars/oscilgen/"] = padpars->oscilgen;
  257. } else {
  258. objmap[base+"padpars/"] = nullptr;
  259. objmap[base+"padpars/oscilgen/"] = nullptr;
  260. }
  261. }
  262. }
  263. void clear(void)
  264. {
  265. objmap.clear();
  266. }
  267. bool has(std::string loc)
  268. {
  269. return objmap.find(loc) != objmap.end();
  270. }
  271. void *get(std::string loc)
  272. {
  273. return objmap[loc];
  274. }
  275. void handleOscil(const char *msg, rtosc::RtData &d) {
  276. string obj_rl(d.message, msg);
  277. void *osc = get(obj_rl);
  278. assert(osc);
  279. strcpy(d.loc, obj_rl.c_str());
  280. d.obj = osc;
  281. OscilGen::non_realtime_ports.dispatch(msg, d);
  282. }
  283. void handlePad(const char *msg, rtosc::RtData &d) {
  284. string obj_rl(d.message, msg);
  285. void *pad = get(obj_rl);
  286. assert(pad);
  287. strcpy(d.loc, obj_rl.c_str());
  288. d.obj = pad;
  289. PADnoteParameters::non_realtime_ports.dispatch(msg, d);
  290. }
  291. };
  292. /******************************************************************************
  293. * Realtime Parameter Store *
  294. * *
  295. * Storage for AD/PAD/SUB parameters which are allocated as needed by kits. *
  296. * Two classes of events affect this: *
  297. * 1. When a message to enable a kit is observed, then the kit is allocated *
  298. * and sent prior to the enable message. *
  299. * 2. When a part is allocated all part information is rebuilt *
  300. * *
  301. * (NOTE pointers aren't really needed here, just booleans on whether it has *
  302. * been allocated) *
  303. * This may be later utilized for copy/paste support *
  304. ******************************************************************************/
  305. struct ParamStore
  306. {
  307. ParamStore(void)
  308. {
  309. memset(add, 0, sizeof(add));
  310. memset(pad, 0, sizeof(pad));
  311. memset(sub, 0, sizeof(sub));
  312. }
  313. void extractPart(Part *part, int i)
  314. {
  315. for(int j=0; j < NUM_KIT_ITEMS; ++j) {
  316. auto kit = part->kit[j];
  317. add[i][j] = kit.adpars;
  318. sub[i][j] = kit.subpars;
  319. pad[i][j] = kit.padpars;
  320. }
  321. }
  322. ADnoteParameters *add[NUM_MIDI_PARTS][NUM_KIT_ITEMS];
  323. SUBnoteParameters *sub[NUM_MIDI_PARTS][NUM_KIT_ITEMS];
  324. PADnoteParameters *pad[NUM_MIDI_PARTS][NUM_KIT_ITEMS];
  325. };
  326. //XXX perhaps move this to Nio
  327. //(there needs to be some standard Nio stub file for this sort of stuff)
  328. namespace Nio
  329. {
  330. using std::get;
  331. using rtosc::rtMsg;
  332. rtosc::Ports ports = {
  333. {"sink-list:", 0, 0, [](const char *, rtosc::RtData &d) {
  334. auto list = Nio::getSinks();
  335. char *ret = rtosc_splat(d.loc, list);
  336. d.reply(ret);
  337. delete [] ret;
  338. }},
  339. {"source-list:", 0, 0, [](const char *, rtosc::RtData &d) {
  340. auto list = Nio::getSources();
  341. char *ret = rtosc_splat(d.loc, list);
  342. d.reply(ret);
  343. delete [] ret;
  344. }},
  345. {"source::s", 0, 0, [](const char *msg, rtosc::RtData &d) {
  346. if(rtosc_narguments(msg) == 0)
  347. d.reply(d.loc, "s", Nio::getSource().c_str());
  348. else if(rtMsg<const char*> m{msg})
  349. Nio::setSource(get<0>(m));}},
  350. {"sink::s", 0, 0, [](const char *msg, rtosc::RtData &d) {
  351. if(rtosc_narguments(msg) == 0)
  352. d.reply(d.loc, "s", Nio::getSink().c_str());
  353. else if(rtMsg<const char*> m{msg})
  354. Nio::setSink(get<0>(m));}},
  355. };
  356. }
  357. /* Implementation */
  358. class MiddleWareImpl
  359. {
  360. public:
  361. MiddleWare *parent;
  362. private:
  363. //Detect if the name of the process is 'zynaddsubfx'
  364. bool isPlugin() const
  365. {
  366. std::string proc_file = "/proc/" + to_s(getpid()) + "/comm";
  367. std::ifstream ifs(proc_file);
  368. if(ifs.good()) {
  369. std::string comm_name;
  370. ifs >> comm_name;
  371. return comm_name != "zynaddsubfx";
  372. }
  373. return true;
  374. }
  375. public:
  376. Config* const config;
  377. MiddleWareImpl(MiddleWare *mw, SYNTH_T synth, Config* config,
  378. int preferred_port);
  379. ~MiddleWareImpl(void);
  380. //Apply function while parameters are write locked
  381. void doReadOnlyOp(std::function<void()> read_only_fn);
  382. void savePart(int npart, const char *filename)
  383. {
  384. //Copy is needed as filename WILL get trashed during the rest of the run
  385. std::string fname = filename;
  386. //printf("saving part(%d,'%s')\n", npart, filename);
  387. doReadOnlyOp([this,fname,npart](){
  388. int res = master->part[npart]->saveXML(fname.c_str());
  389. (void)res;
  390. /*printf("results: '%s' '%d'\n",fname.c_str(), res);*/});
  391. }
  392. void loadPendingBank(int par, Bank &bank)
  393. {
  394. if(((unsigned int)par < bank.banks.size())
  395. && (bank.banks[par].dir != bank.bankfiletitle))
  396. bank.loadbank(bank.banks[par].dir);
  397. }
  398. void loadPart(int npart, const char *filename, Master *master)
  399. {
  400. actual_load[npart]++;
  401. if(actual_load[npart] != pending_load[npart])
  402. return;
  403. assert(actual_load[npart] <= pending_load[npart]);
  404. //load part in async fashion when possible
  405. #if HAVE_ASYNC
  406. auto alloc = std::async(std::launch::async,
  407. [master,filename,this,npart](){
  408. Part *p = new Part(*master->memory, synth,
  409. master->time,
  410. config->cfg.GzipCompression,
  411. config->cfg.Interpolation,
  412. &master->microtonal, master->fft);
  413. if(p->loadXMLinstrument(filename))
  414. fprintf(stderr, "Warning: failed to load part<%s>!\n", filename);
  415. auto isLateLoad = [this,npart]{
  416. return actual_load[npart] != pending_load[npart];
  417. };
  418. p->applyparameters(isLateLoad);
  419. return p;});
  420. //Load the part
  421. if(idle) {
  422. while(alloc.wait_for(std::chrono::seconds(0)) != std::future_status::ready) {
  423. idle(idle_ptr);
  424. }
  425. }
  426. Part *p = alloc.get();
  427. #else
  428. Part *p = new Part(*master->memory, synth, master->time,
  429. config->cfg.GzipCompression,
  430. config->cfg.Interpolation,
  431. &master->microtonal, master->fft);
  432. if(p->loadXMLinstrument(filename))
  433. fprintf(stderr, "Warning: failed to load part<%s>!\n", filename);
  434. auto isLateLoad = [this,npart]{
  435. return actual_load[npart] != pending_load[npart];
  436. };
  437. p->applyparameters(isLateLoad);
  438. #endif
  439. obj_store.extractPart(p, npart);
  440. kits.extractPart(p, npart);
  441. //Give it to the backend and wait for the old part to return for
  442. //deallocation
  443. parent->transmitMsg("/load-part", "ib", npart, sizeof(Part*), &p);
  444. GUI::raiseUi(ui, "/damage", "s", ("/part"+to_s(npart)+"/").c_str());
  445. }
  446. //Load a new cleared Part instance
  447. void loadClearPart(int npart)
  448. {
  449. if(npart == -1)
  450. return;
  451. Part *p = new Part(*master->memory, synth,
  452. master->time,
  453. config->cfg.GzipCompression,
  454. config->cfg.Interpolation,
  455. &master->microtonal, master->fft);
  456. p->applyparameters();
  457. obj_store.extractPart(p, npart);
  458. kits.extractPart(p, npart);
  459. //Give it to the backend and wait for the old part to return for
  460. //deallocation
  461. parent->transmitMsg("/load-part", "ib", npart, sizeof(Part*), &p);
  462. GUI::raiseUi(ui, "/damage", "s", ("/part"+to_s(npart)+"/").c_str());
  463. }
  464. //Well, you don't get much crazier than changing out all of your RT
  465. //structures at once... TODO error handling
  466. void loadMaster(const char *filename)
  467. {
  468. Master *m = new Master(synth, config);
  469. m->uToB = uToB;
  470. m->bToU = bToU;
  471. if(filename) {
  472. if ( m->loadXML(filename) ) {
  473. delete m;
  474. return;
  475. }
  476. m->applyparameters();
  477. }
  478. //Update resource locator table
  479. updateResources(m);
  480. master = m;
  481. //Give it to the backend and wait for the old part to return for
  482. //deallocation
  483. parent->transmitMsg("/load-master", "b", sizeof(Master*), &m);
  484. }
  485. void updateResources(Master *m)
  486. {
  487. obj_store.clear();
  488. obj_store.extractMaster(m);
  489. for(int i=0; i<NUM_MIDI_PARTS; ++i)
  490. kits.extractPart(m->part[i], i);
  491. }
  492. //If currently broadcasting messages
  493. bool broadcast = false;
  494. //If message should be forwarded through snoop ports
  495. bool forward = false;
  496. //if message is in order or out-of-order execution
  497. bool in_order = false;
  498. //If accepting undo events as user driven
  499. bool recording_undo = true;
  500. void bToUhandle(const char *rtmsg);
  501. void tick(void)
  502. {
  503. if(server)
  504. while(lo_server_recv_noblock(server, 0));
  505. while(bToU->hasNext()) {
  506. const char *rtmsg = bToU->read();
  507. bToUhandle(rtmsg);
  508. }
  509. }
  510. void kitEnable(const char *msg);
  511. void kitEnable(int part, int kit, int type);
  512. // Handle an event with special cases
  513. void handleMsg(const char *msg);
  514. void write(const char *path, const char *args, ...);
  515. void write(const char *path, const char *args, va_list va);
  516. // Send a message to a remote client
  517. void sendToRemote(const char *msg, std::string dest);
  518. // Send a message to the current remote client
  519. void sendToCurrentRemote(const char *msg)
  520. {
  521. sendToRemote(msg, in_order ? curr_url : last_url);
  522. }
  523. // Broadcast a message to all listening remote clients
  524. void broadcastToRemote(const char *msg);
  525. /*
  526. * Provides a mapping for non-RT objects stored inside the backend
  527. * - Oscilgen almost all parameters can be safely set
  528. * - Padnote can have anything set on its oscilgen and a very small set
  529. * of general parameters
  530. */
  531. NonRtObjStore obj_store;
  532. //This code will own the pointer to master, be prepared for odd things if
  533. //this assumption is broken
  534. Master *master;
  535. //The ONLY means that any chunk of UI code should have for interacting with the
  536. //backend
  537. Fl_Osc_Interface *osc;
  538. //Synth Engine Parameters
  539. ParamStore kits;
  540. //Callback When Waiting on async events
  541. void(*idle)(void*);
  542. void* idle_ptr;
  543. //General UI callback
  544. cb_t cb;
  545. //UI handle
  546. void *ui;
  547. std::atomic_int pending_load[NUM_MIDI_PARTS];
  548. std::atomic_int actual_load[NUM_MIDI_PARTS];
  549. //Undo/Redo
  550. rtosc::UndoHistory undo;
  551. //MIDI Learn
  552. rtosc::MidiMappernRT midi_mapper;
  553. //Link To the Realtime
  554. rtosc::ThreadLink *bToU;
  555. rtosc::ThreadLink *uToB;
  556. //LIBLO
  557. lo_server server;
  558. string last_url, curr_url;
  559. //Synthesis Rate Parameters
  560. const SYNTH_T synth;
  561. PresetsStore presetsstore;
  562. };
  563. /*****************************************************************************
  564. * Data Object for Non-RT Class Dispatch *
  565. *****************************************************************************/
  566. class MwDataObj:public rtosc::RtData
  567. {
  568. public:
  569. MwDataObj(MiddleWareImpl *mwi_)
  570. {
  571. loc_size = 1024;
  572. loc = new char[loc_size];
  573. memset(loc, 0, loc_size);
  574. buffer = new char[4*4096];
  575. memset(buffer, 0, 4*4096);
  576. obj = mwi_;
  577. mwi = mwi_;
  578. forwarded = false;
  579. }
  580. ~MwDataObj(void)
  581. {
  582. delete[] buffer;
  583. }
  584. //Replies and broadcasts go to the remote
  585. //Chain calls repeat the call into handle()
  586. //Forward calls send the message directly to the realtime
  587. virtual void reply(const char *path, const char *args, ...)
  588. {
  589. //printf("reply building '%s'\n", path);
  590. va_list va;
  591. va_start(va,args);
  592. if(!strcmp(path, "/forward")) { //forward the information to the backend
  593. args++;
  594. path = va_arg(va, const char *);
  595. rtosc_vmessage(buffer,4*4096,path,args,va);
  596. } else {
  597. rtosc_vmessage(buffer,4*4096,path,args,va);
  598. reply(buffer);
  599. }
  600. va_end(va);
  601. }
  602. virtual void reply(const char *msg){
  603. mwi->sendToCurrentRemote(msg);
  604. };
  605. //virtual void broadcast(const char *path, const char *args, ...){(void)path;(void)args;};
  606. //virtual void broadcast(const char *msg){(void)msg;};
  607. virtual void chain(const char *msg) override
  608. {
  609. assert(msg);
  610. // printf("chain call on <%s>\n", msg);
  611. mwi->handleMsg(msg);
  612. }
  613. virtual void chain(const char *path, const char *args, ...) override
  614. {
  615. assert(path);
  616. va_list va;
  617. va_start(va,args);
  618. rtosc_vmessage(buffer,4*4096,path,args,va);
  619. chain(buffer);
  620. va_end(va);
  621. }
  622. virtual void forward(const char *) override
  623. {
  624. forwarded = true;
  625. }
  626. bool forwarded;
  627. private:
  628. char *buffer;
  629. MiddleWareImpl *mwi;
  630. };
  631. static int extractInt(const char *msg)
  632. {
  633. const char *mm = msg;
  634. while(*mm && !isdigit(*mm)) ++mm;
  635. if(isdigit(*mm))
  636. return atoi(mm);
  637. return -1;
  638. }
  639. static const char *chomp(const char *msg)
  640. {
  641. while(*msg && *msg!='/') ++msg; \
  642. msg = *msg ? msg+1 : msg;
  643. return msg;
  644. };
  645. using rtosc::RtData;
  646. #define rObject Bank
  647. #define rBegin [](const char *msg, RtData &d) { (void)msg;(void)d;\
  648. rObject &impl = *((rObject*)d.obj);(void)impl;
  649. #define rEnd }
  650. /*****************************************************************************
  651. * Instrument Banks *
  652. * *
  653. * Banks and presets in general are not classed as realtime safe *
  654. * *
  655. * The supported operations are: *
  656. * - Load Names *
  657. * - Load Bank *
  658. * - Refresh List of Banks *
  659. *****************************************************************************/
  660. rtosc::Ports bankPorts = {
  661. {"rescan:", 0, 0,
  662. rBegin;
  663. impl.rescanforbanks();
  664. //Send updated banks
  665. int i = 0;
  666. for(auto &elm : impl.banks)
  667. d.reply("/bank/bank_select", "iss", i++, elm.name.c_str(), elm.dir.c_str());
  668. rEnd},
  669. {"slot#1024:", 0, 0,
  670. rBegin;
  671. const int loc = extractInt(msg);
  672. if(loc >= BANK_SIZE)
  673. return;
  674. d.reply("/bankview", "iss",
  675. loc, impl.ins[loc].name.c_str(),
  676. impl.ins[loc].filename.c_str());
  677. rEnd},
  678. {"banks:", 0, 0,
  679. rBegin;
  680. int i = 0;
  681. for(auto &elm : impl.banks)
  682. d.reply("/bank/bank_select", "iss", i++, elm.name.c_str(), elm.dir.c_str());
  683. rEnd},
  684. {"bank_select::i", 0, 0,
  685. rBegin
  686. if(rtosc_narguments(msg)) {
  687. const int pos = rtosc_argument(msg, 0).i;
  688. d.reply(d.loc, "i", pos);
  689. if(impl.bankpos != pos) {
  690. impl.bankpos = pos;
  691. impl.loadbank(impl.banks[pos].dir);
  692. //Reload bank slots
  693. for(int i=0; i<BANK_SIZE; ++i)
  694. d.reply("/bankview", "iss",
  695. i, impl.ins[i].name.c_str(),
  696. impl.ins[i].filename.c_str());
  697. }
  698. } else
  699. d.reply("/bank/bank_select", "i", impl.bankpos);
  700. rEnd},
  701. {"rename_slot:is", 0, 0,
  702. rBegin;
  703. const int slot = rtosc_argument(msg, 0).i;
  704. const char *name = rtosc_argument(msg, 1).s;
  705. const int err = impl.setname(slot, name, -1);
  706. if(err) {
  707. d.reply("/alert", "s",
  708. "Failed To Rename Bank Slot, please check file permissions");
  709. }
  710. rEnd},
  711. {"swap_slots:ii", 0, 0,
  712. rBegin;
  713. const int slota = rtosc_argument(msg, 0).i;
  714. const int slotb = rtosc_argument(msg, 1).i;
  715. const int err = impl.swapslot(slota, slotb);
  716. if(err)
  717. d.reply("/alert", "s",
  718. "Failed To Swap Bank Slots, please check file permissions");
  719. rEnd},
  720. {"clear_slot:i", 0, 0,
  721. rBegin;
  722. const int slot = rtosc_argument(msg, 0).i;
  723. const int err = impl.clearslot(slot);
  724. if(err)
  725. d.reply("/alert", "s",
  726. "Failed To Clear Bank Slot, please check file permissions");
  727. rEnd},
  728. };
  729. /******************************************************************************
  730. * MiddleWare Snooping Ports *
  731. * *
  732. * These ports handle: *
  733. * - Events going to the realtime thread which cannot be safely handled *
  734. * there *
  735. * - Events generated by the realtime thread which are not destined for a *
  736. * user interface *
  737. ******************************************************************************/
  738. #undef rObject
  739. #define rObject MiddleWareImpl
  740. /*
  741. * BASE/part#/kititem#
  742. * BASE/part#/kit#/adpars/voice#/oscil/\*
  743. * BASE/part#/kit#/adpars/voice#/mod-oscil/\*
  744. * BASE/part#/kit#/padpars/prepare
  745. * BASE/part#/kit#/padpars/oscil/\*
  746. */
  747. static rtosc::Ports middwareSnoopPorts = {
  748. {"part#16/kit#8/adpars/VoicePar#8/OscilSmp/", 0, &OscilGen::non_realtime_ports,
  749. rBegin;
  750. impl.obj_store.handleOscil(chomp(chomp(chomp(chomp(chomp(msg))))), d);
  751. rEnd},
  752. {"part#16/kit#8/adpars/VoicePar#8/FMSmp/", 0, &OscilGen::non_realtime_ports,
  753. rBegin
  754. impl.obj_store.handleOscil(chomp(chomp(chomp(chomp(chomp(msg))))), d);
  755. rEnd},
  756. {"part#16/kit#8/padpars/", 0, &PADnoteParameters::non_realtime_ports,
  757. rBegin
  758. impl.obj_store.handlePad(chomp(chomp(chomp(msg))), d);
  759. rEnd},
  760. {"bank/", 0, &bankPorts,
  761. rBegin;
  762. d.obj = &impl.master->bank;
  763. bankPorts.dispatch(chomp(msg),d);
  764. rEnd},
  765. {"bank/save_to_slot:ii", 0, 0,
  766. rBegin;
  767. const int part_id = rtosc_argument(msg, 0).i;
  768. const int slot = rtosc_argument(msg, 1).i;
  769. int err = 0;
  770. impl.doReadOnlyOp([&impl,slot,part_id,&err](){
  771. err = impl.master->bank.savetoslot(slot, impl.master->part[part_id]);});
  772. if(err) {
  773. char buffer[1024];
  774. rtosc_message(buffer, 1024, "/alert", "s",
  775. "Failed To Save To Bank Slot, please check file permissions");
  776. GUI::raiseUi(impl.ui, buffer);
  777. }
  778. rEnd},
  779. {"config/", 0, &Config::ports,
  780. rBegin;
  781. d.obj = impl.config;
  782. Config::ports.dispatch(chomp(msg), d);
  783. rEnd},
  784. {"presets/", 0, &real_preset_ports, [](const char *msg, RtData &d) {
  785. MiddleWareImpl *obj = (MiddleWareImpl*)d.obj;
  786. d.obj = (void*)obj->parent;
  787. real_preset_ports.dispatch(chomp(msg), d);
  788. if(strstr(msg, "paste") && rtosc_argument_string(msg)[0] == 's')
  789. d.reply("/damage", "s", rtosc_argument(msg, 0).s);
  790. }},
  791. {"io/", 0, &Nio::ports, [](const char *msg, RtData &d) {
  792. Nio::ports.dispatch(chomp(msg), d);}},
  793. {"part*/kit*/{Padenabled,Ppadenabled,Psubenabled}:T:F", 0, 0,
  794. rBegin;
  795. impl.kitEnable(msg);
  796. d.forward();
  797. rEnd},
  798. {"save_xlz:s", 0, 0,
  799. rBegin;
  800. const char *file = rtosc_argument(msg, 0).s;
  801. XMLwrapper xml;
  802. saveMidiLearn(xml, impl.midi_mapper);
  803. xml.saveXMLfile(file, impl.master->gzip_compression);
  804. rEnd},
  805. {"load_xlz:s", 0, 0,
  806. rBegin;
  807. const char *file = rtosc_argument(msg, 0).s;
  808. XMLwrapper xml;
  809. xml.loadXMLfile(file);
  810. loadMidiLearn(xml, impl.midi_mapper);
  811. rEnd},
  812. {"save_xmz:s", 0, 0,
  813. rBegin;
  814. const char *file = rtosc_argument(msg, 0).s;
  815. //Copy is needed as filename WILL get trashed during the rest of the run
  816. impl.doReadOnlyOp([&impl,file](){
  817. int res = impl.master->saveXML(file);
  818. (void)res;});
  819. rEnd},
  820. {"save_xiz:is", 0, 0,
  821. rBegin;
  822. const int part_id = rtosc_argument(msg,0).i;
  823. const char *file = rtosc_argument(msg,1).s;
  824. impl.savePart(part_id, file);
  825. rEnd},
  826. {"load_xmz:s", 0, 0,
  827. rBegin;
  828. const char *file = rtosc_argument(msg, 0).s;
  829. impl.loadMaster(file);
  830. rEnd},
  831. {"reset_master:", 0, 0,
  832. rBegin;
  833. impl.loadMaster(NULL);
  834. rEnd},
  835. {"load_xiz:is", 0, 0,
  836. rBegin;
  837. const int part_id = rtosc_argument(msg,0).i;
  838. const char *file = rtosc_argument(msg,1).s;
  839. impl.pending_load[part_id]++;
  840. impl.loadPart(part_id, file, impl.master);
  841. rEnd},
  842. {"load-part:is", 0, 0,
  843. rBegin;
  844. const int part_id = rtosc_argument(msg,0).i;
  845. const char *file = rtosc_argument(msg,1).s;
  846. impl.pending_load[part_id]++;
  847. impl.loadPart(part_id, file, impl.master);
  848. rEnd},
  849. {"setprogram:i:c", 0, 0,
  850. rBegin;
  851. const int slot = rtosc_argument(msg, 0).i;
  852. impl.pending_load[0]++;
  853. impl.loadPart(0, impl.master->bank.ins[slot].filename.c_str(), impl.master);
  854. rEnd},
  855. {"part#16/clear:", 0, 0,
  856. rBegin;
  857. impl.loadClearPart(extractInt(msg));
  858. rEnd},
  859. {"undo:", 0, 0,
  860. rBegin;
  861. impl.undo.seekHistory(-1);
  862. rEnd},
  863. {"redo:", 0, 0,
  864. rBegin;
  865. impl.undo.seekHistory(+1);
  866. rEnd},
  867. {"learn:s", 0, 0,
  868. rBegin;
  869. string addr = rtosc_argument(msg, 0).s;
  870. auto &midi = impl.midi_mapper;
  871. auto map = midi.getMidiMappingStrings();
  872. if(map.find(addr) != map.end())
  873. midi.map(addr.c_str(), false);
  874. else
  875. midi.map(addr.c_str(), true);
  876. rEnd},
  877. //drop this message into the abyss
  878. {"ui/title:", 0, 0, [](const char *msg, RtData &d) {}}
  879. };
  880. static rtosc::Ports middlewareReplyPorts = {
  881. {"echo:ss", 0, 0,
  882. rBegin;
  883. const char *type = rtosc_argument(msg, 0).s;
  884. const char *url = rtosc_argument(msg, 1).s;
  885. if(!strcmp(type, "OSC_URL"))
  886. impl.curr_url = url;
  887. rEnd},
  888. {"free:sb", 0, 0,
  889. rBegin;
  890. const char *type = rtosc_argument(msg, 0).s;
  891. void *ptr = *(void**)rtosc_argument(msg, 1).b.data;
  892. deallocate(type, ptr);
  893. rEnd},
  894. {"request_memory:", 0, 0,
  895. rBegin;
  896. //Generate out more memory for the RT memory pool
  897. //5MBi chunk
  898. size_t N = 5*1024*1024;
  899. void *mem = malloc(N);
  900. impl.uToB->write("/add-rt-memory", "bi", sizeof(void*), &mem, N);
  901. rEnd},
  902. {"setprogram:cc:ii", 0, 0,
  903. rBegin;
  904. const int part = rtosc_argument(msg, 0).i;
  905. const int program = rtosc_argument(msg, 1).i;
  906. impl.loadPart(part, impl.master->bank.ins[program].filename.c_str(), impl.master);
  907. rEnd},
  908. {"undo_pause:", 0, 0, rBegin; impl.recording_undo = false; rEnd},
  909. {"undo_resume:", 0, 0, rBegin; impl.recording_undo = true; rEnd},
  910. {"undo_change", 0, 0,
  911. rBegin;
  912. if(impl.recording_undo)
  913. impl.undo.recordEvent(msg);
  914. rEnd},
  915. {"midi-use-CC:i", 0, 0,
  916. rBegin;
  917. impl.midi_mapper.useFreeID(rtosc_argument(msg, 0).i);
  918. rEnd},
  919. {"broadcast:", 0, 0, rBegin; impl.broadcast = true; rEnd},
  920. {"forward:", 0, 0, rBegin; impl.forward = true; rEnd},
  921. };
  922. #undef rBegin
  923. #undef rEnd
  924. /******************************************************************************
  925. * MiddleWare Implementation *
  926. ******************************************************************************/
  927. MiddleWareImpl::MiddleWareImpl(MiddleWare *mw, SYNTH_T synth_,
  928. Config* config, int preferrred_port)
  929. :parent(mw), config(config), ui(nullptr), synth(std::move(synth_)),
  930. presetsstore(*config)
  931. {
  932. bToU = new rtosc::ThreadLink(4096*2,1024);
  933. uToB = new rtosc::ThreadLink(4096*2,1024);
  934. midi_mapper.base_ports = &Master::ports;
  935. midi_mapper.rt_cb = [this](const char *msg){handleMsg(msg);};
  936. if(preferrred_port != -1)
  937. server = lo_server_new_with_proto(to_s(preferrred_port).c_str(),
  938. LO_UDP, liblo_error_cb);
  939. else
  940. server = lo_server_new_with_proto(NULL, LO_UDP, liblo_error_cb);
  941. if(server) {
  942. lo_server_add_method(server, NULL, NULL, handler_function, mw);
  943. fprintf(stderr, "lo server running on %d\n", lo_server_get_port(server));
  944. } else
  945. fprintf(stderr, "lo server could not be started :-/\n");
  946. //dummy callback for starters
  947. cb = [](void*, const char*){};
  948. idle = 0;
  949. idle_ptr = 0;
  950. master = new Master(synth, config);
  951. master->bToU = bToU;
  952. master->uToB = uToB;
  953. osc = GUI::genOscInterface(mw);
  954. //Grab objects of interest from master
  955. updateResources(master);
  956. //Null out Load IDs
  957. for(int i=0; i < NUM_MIDI_PARTS; ++i) {
  958. pending_load[i] = 0;
  959. actual_load[i] = 0;
  960. }
  961. //Setup Undo
  962. undo.setCallback([this](const char *msg) {
  963. // printf("undo callback <%s>\n", msg);
  964. char buf[1024];
  965. rtosc_message(buf, 1024, "/undo_pause","");
  966. handleMsg(buf);
  967. handleMsg(msg);
  968. rtosc_message(buf, 1024, "/undo_resume","");
  969. handleMsg(buf);
  970. });
  971. }
  972. MiddleWareImpl::~MiddleWareImpl(void)
  973. {
  974. if(server)
  975. lo_server_free(server);
  976. delete master;
  977. delete osc;
  978. delete bToU;
  979. delete uToB;
  980. }
  981. /** Threading When Saving
  982. * ----------------------
  983. *
  984. * Procedure Middleware:
  985. * 1) Middleware sends /freeze_state to backend
  986. * 2) Middleware waits on /state_frozen from backend
  987. * All intervening commands are held for out of order execution
  988. * 3) Aquire memory
  989. * At this time by the memory barrier we are guarenteed that all old
  990. * writes are done and assuming the freezing logic is sound, then it is
  991. * impossible for any other parameter to change at this time
  992. * 3) Middleware performs saving operation
  993. * 4) Middleware sends /thaw_state to backend
  994. * 5) Restore in order execution
  995. *
  996. * Procedure Backend:
  997. * 1) Observe /freeze_state and disable all mutating events (MIDI CC)
  998. * 2) Run a memory release to ensure that all writes are complete
  999. * 3) Send /state_frozen to Middleware
  1000. * time...
  1001. * 4) Observe /thaw_state and resume normal processing
  1002. */
  1003. void MiddleWareImpl::doReadOnlyOp(std::function<void()> read_only_fn)
  1004. {
  1005. assert(uToB);
  1006. uToB->write("/freeze_state","");
  1007. std::list<const char *> fico;
  1008. int tries = 0;
  1009. while(tries++ < 10000) {
  1010. if(!bToU->hasNext()) {
  1011. usleep(500);
  1012. continue;
  1013. }
  1014. const char *msg = bToU->read();
  1015. if(!strcmp("/state_frozen", msg))
  1016. break;
  1017. size_t bytes = rtosc_message_length(msg, bToU->buffer_size());
  1018. char *save_buf = new char[bytes];
  1019. memcpy(save_buf, msg, bytes);
  1020. fico.push_back(save_buf);
  1021. }
  1022. assert(tries < 10000);//if this happens, the backend must be dead
  1023. std::atomic_thread_fence(std::memory_order_acquire);
  1024. //Now it is safe to do any read only operation
  1025. read_only_fn();
  1026. //Now to resume normal operations
  1027. uToB->write("/thaw_state","");
  1028. for(auto x:fico) {
  1029. uToB->raw_write(x);
  1030. delete [] x;
  1031. }
  1032. }
  1033. void MiddleWareImpl::broadcastToRemote(const char *rtmsg)
  1034. {
  1035. //Always send to the local UI
  1036. sendToRemote(rtmsg, "GUI");
  1037. //Send to remote UI if there's one listening
  1038. if(curr_url != "GUI")
  1039. sendToRemote(rtmsg, curr_url);
  1040. broadcast = false;
  1041. }
  1042. void MiddleWareImpl::sendToRemote(const char *rtmsg, std::string dest)
  1043. {
  1044. //printf("sendToRemote(%s:%s,%s)\n", rtmsg, rtosc_argument_string(rtmsg),
  1045. // dest.c_str());
  1046. if(dest == "GUI") {
  1047. cb(ui, rtmsg);
  1048. } else if(!dest.empty()) {
  1049. lo_message msg = lo_message_deserialise((void*)rtmsg,
  1050. rtosc_message_length(rtmsg, bToU->buffer_size()), NULL);
  1051. //Send to known url
  1052. lo_address addr = lo_address_new_from_url(dest.c_str());
  1053. if(addr)
  1054. lo_send_message(addr, rtmsg, msg);
  1055. }
  1056. }
  1057. /**
  1058. * Handle all events coming from the backend
  1059. *
  1060. * This includes forwarded events which need to be retransmitted to the backend
  1061. * after the snooping code inspects the message
  1062. */
  1063. void MiddleWareImpl::bToUhandle(const char *rtmsg)
  1064. {
  1065. //Verify Message isn't a known corruption bug
  1066. assert(strcmp(rtmsg, "/part0/kit0/Ppadenableda"));
  1067. assert(strcmp(rtmsg, "/ze_state"));
  1068. //Dump Incomming Events For Debugging
  1069. if(strcmp(rtmsg, "/vu-meter") && false) {
  1070. fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 1 + 30, 0 + 40);
  1071. fprintf(stdout, "frontend[%c]: '%s'<%s>\n", forward?'f':broadcast?'b':'N',
  1072. rtmsg, rtosc_argument_string(rtmsg));
  1073. fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  1074. }
  1075. //Activity dot
  1076. //printf(".");fflush(stdout);
  1077. MwDataObj d(this);
  1078. middlewareReplyPorts.dispatch(rtmsg, d, true);
  1079. in_order = true;
  1080. //Normal message not captured by the ports
  1081. if(d.matches == 0) {
  1082. if(forward) {
  1083. forward = false;
  1084. handleMsg(rtmsg);
  1085. } if(broadcast)
  1086. broadcastToRemote(rtmsg);
  1087. else
  1088. sendToCurrentRemote(rtmsg);
  1089. }
  1090. in_order = false;
  1091. }
  1092. //Allocate kits on a as needed basis
  1093. void MiddleWareImpl::kitEnable(const char *msg)
  1094. {
  1095. const string argv = rtosc_argument_string(msg);
  1096. if(argv != "T")
  1097. return;
  1098. //Extract fields from:
  1099. //BASE/part#/kit#/Pxxxenabled
  1100. int type = -1;
  1101. if(strstr(msg, "Padenabled"))
  1102. type = 0;
  1103. else if(strstr(msg, "Ppadenabled"))
  1104. type = 1;
  1105. else if(strstr(msg, "Psubenabled"))
  1106. type = 2;
  1107. else
  1108. return;
  1109. const char *tmp = strstr(msg, "part");
  1110. if(tmp == NULL)
  1111. return;
  1112. const int part = atoi(tmp+4);
  1113. tmp = strstr(msg, "kit");
  1114. if(tmp == NULL)
  1115. return;
  1116. const int kit = atoi(tmp+3);
  1117. kitEnable(part, kit, type);
  1118. }
  1119. void MiddleWareImpl::kitEnable(int part, int kit, int type)
  1120. {
  1121. //printf("attempting a kit enable<%d,%d,%d>\n", part, kit, type);
  1122. string url = "/part"+to_s(part)+"/kit"+to_s(kit)+"/";
  1123. void *ptr = NULL;
  1124. if(type == 0 && kits.add[part][kit] == NULL) {
  1125. ptr = kits.add[part][kit] = new ADnoteParameters(synth, master->fft);
  1126. url += "adpars-data";
  1127. obj_store.extractAD(kits.add[part][kit], part, kit);
  1128. } else if(type == 1 && kits.pad[part][kit] == NULL) {
  1129. ptr = kits.pad[part][kit] = new PADnoteParameters(synth, master->fft);
  1130. url += "padpars-data";
  1131. obj_store.extractPAD(kits.pad[part][kit], part, kit);
  1132. } else if(type == 2 && kits.sub[part][kit] == NULL) {
  1133. ptr = kits.sub[part][kit] = new SUBnoteParameters();
  1134. url += "subpars-data";
  1135. }
  1136. //Send the new memory
  1137. if(ptr)
  1138. uToB->write(url.c_str(), "b", sizeof(void*), &ptr);
  1139. }
  1140. /*
  1141. * Handle all messages traveling to the realtime side.
  1142. */
  1143. void MiddleWareImpl::handleMsg(const char *msg)
  1144. {
  1145. //Check for known bugs
  1146. assert(msg && *msg && rindex(msg, '/')[1]);
  1147. assert(strstr(msg,"free") == NULL || strstr(rtosc_argument_string(msg), "b") == NULL);
  1148. assert(strcmp(msg, "/part0/Psysefxvol"));
  1149. assert(strcmp(msg, "/Penabled"));
  1150. assert(strcmp(msg, "part0/part0/Ppanning"));
  1151. assert(strcmp(msg, "sysefx0sysefx0/preset"));
  1152. assert(strcmp(msg, "/sysefx0preset"));
  1153. assert(strcmp(msg, "Psysefxvol0/part0"));
  1154. if(strcmp("/get-vu", msg) && false) {
  1155. fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 6 + 30, 0 + 40);
  1156. fprintf(stdout, "middleware: '%s':%s\n", msg, rtosc_argument_string(msg));
  1157. fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  1158. }
  1159. const char *last_path = rindex(msg, '/');
  1160. if(!last_path) {
  1161. printf("Bad message in handleMsg() <%s>\n", msg);
  1162. assert(false);
  1163. return;
  1164. }
  1165. MwDataObj d(this);
  1166. middwareSnoopPorts.dispatch(msg, d, true);
  1167. //A message unmodified by snooping
  1168. if(d.matches == 0 || d.forwarded) {
  1169. //if(strcmp("/get-vu", msg)) {
  1170. // printf("Message Continuing on<%s:%s>...\n", msg, rtosc_argument_string(msg));
  1171. //}
  1172. uToB->raw_write(msg);
  1173. } else {
  1174. //printf("Message Handled<%s:%s>...\n", msg, rtosc_argument_string(msg));
  1175. }
  1176. }
  1177. void MiddleWareImpl::write(const char *path, const char *args, ...)
  1178. {
  1179. //We have a free buffer in the threadlink, so use it
  1180. va_list va;
  1181. va_start(va, args);
  1182. write(path, args, va);
  1183. va_end(va);
  1184. }
  1185. void MiddleWareImpl::write(const char *path, const char *args, va_list va)
  1186. {
  1187. //printf("is that a '%s' I see there?\n", path);
  1188. char *buffer = uToB->buffer();
  1189. unsigned len = uToB->buffer_size();
  1190. bool success = rtosc_vmessage(buffer, len, path, args, va);
  1191. //printf("working on '%s':'%s'\n",path, args);
  1192. if(success)
  1193. handleMsg(buffer);
  1194. else
  1195. warnx("Failed to write message to '%s'", path);
  1196. }
  1197. /******************************************************************************
  1198. * MidleWare Forwarding Stubs *
  1199. ******************************************************************************/
  1200. MiddleWare::MiddleWare(SYNTH_T synth, Config* config,
  1201. int preferred_port)
  1202. :impl(new MiddleWareImpl(this, std::move(synth), config, preferred_port))
  1203. {}
  1204. MiddleWare::~MiddleWare(void)
  1205. {
  1206. delete impl;
  1207. }
  1208. void MiddleWare::updateResources(Master *m)
  1209. {
  1210. impl->updateResources(m);
  1211. }
  1212. Master *MiddleWare::spawnMaster(void)
  1213. {
  1214. assert(impl->master);
  1215. assert(impl->master->uToB);
  1216. return impl->master;
  1217. }
  1218. Fl_Osc_Interface *MiddleWare::spawnUiApi(void)
  1219. {
  1220. return impl->osc;
  1221. }
  1222. void MiddleWare::tick(void)
  1223. {
  1224. impl->tick();
  1225. }
  1226. void MiddleWare::doReadOnlyOp(std::function<void()> fn)
  1227. {
  1228. impl->doReadOnlyOp(fn);
  1229. }
  1230. void MiddleWare::setUiCallback(void(*cb)(void*,const char *), void *ui)
  1231. {
  1232. impl->cb = cb;
  1233. impl->ui = ui;
  1234. }
  1235. void MiddleWare::setIdleCallback(void(*cb)(void*), void *ptr)
  1236. {
  1237. impl->idle = cb;
  1238. impl->idle_ptr = ptr;
  1239. }
  1240. void MiddleWare::transmitMsg(const char *msg)
  1241. {
  1242. impl->handleMsg(msg);
  1243. }
  1244. void MiddleWare::transmitMsg(const char *path, const char *args, ...)
  1245. {
  1246. char buffer[1024];
  1247. va_list va;
  1248. va_start(va,args);
  1249. if(rtosc_vmessage(buffer,1024,path,args,va))
  1250. transmitMsg(buffer);
  1251. else
  1252. fprintf(stderr, "Error in transmitMsg(...)\n");
  1253. va_end(va);
  1254. }
  1255. void MiddleWare::transmitMsg_va(const char *path, const char *args, va_list va)
  1256. {
  1257. char buffer[1024];
  1258. if(rtosc_vmessage(buffer, 1024, path, args, va))
  1259. transmitMsg(buffer);
  1260. else
  1261. fprintf(stderr, "Error in transmitMsg(va)n");
  1262. }
  1263. void MiddleWare::pendingSetBank(int bank)
  1264. {
  1265. impl->bToU->write("/setbank", "c", bank);
  1266. }
  1267. void MiddleWare::pendingSetProgram(int part, int program)
  1268. {
  1269. impl->pending_load[part]++;
  1270. impl->bToU->write("/setprogram", "cc", part, program);
  1271. }
  1272. std::string MiddleWare::activeUrl(void)
  1273. {
  1274. return impl->last_url;
  1275. }
  1276. void MiddleWare::activeUrl(std::string u)
  1277. {
  1278. impl->last_url = u;
  1279. }
  1280. const SYNTH_T &MiddleWare::getSynth(void) const
  1281. {
  1282. return impl->synth;
  1283. }
  1284. const char* MiddleWare::getServerAddress(void) const
  1285. {
  1286. if(impl->server)
  1287. return lo_server_get_url(impl->server);
  1288. else
  1289. return "NULL";
  1290. }
  1291. const PresetsStore& MiddleWare::getPresetsStore() const
  1292. {
  1293. return impl->presetsstore;
  1294. }
  1295. PresetsStore& MiddleWare::getPresetsStore()
  1296. {
  1297. return impl->presetsstore;
  1298. }