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.

1361 lines
45KB

  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 <lo/lo.h>
  10. #include <unistd.h>
  11. #include <dirent.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 <string>
  26. #include <future>
  27. #include <atomic>
  28. #include <list>
  29. #include <err.h>
  30. using std::string;
  31. #ifndef PLUGINVERSION
  32. extern rtosc::ThreadLink *the_bToU;//XXX
  33. #endif
  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(buffer);
  117. return 0;
  118. }
  119. typedef void(*cb_t)(void*,const char*);
  120. /*****************************************************************************
  121. * Memory Deallocation *
  122. *****************************************************************************/
  123. void deallocate(const char *str, void *v)
  124. {
  125. //printf("deallocating a '%s' at '%p'\n", str, v);
  126. if(!strcmp(str, "Part"))
  127. delete (Part*)v;
  128. else if(!strcmp(str, "Master"))
  129. delete (Master*)v;
  130. else if(!strcmp(str, "fft_t"))
  131. delete[] (fft_t*)v;
  132. else
  133. fprintf(stderr, "Unknown type '%s', leaking pointer %p!!\n", str, v);
  134. }
  135. /*****************************************************************************
  136. * PadSynth Setup *
  137. *****************************************************************************/
  138. void preparePadSynth(string path, PADnoteParameters *p, rtosc::ThreadLink *uToB)
  139. {
  140. //printf("preparing padsynth parameters\n");
  141. assert(!path.empty());
  142. path += "sample";
  143. unsigned max = 0;
  144. p->sampleGenerator([&max,&path,uToB]
  145. (unsigned N, PADnoteParameters::Sample &s)
  146. {
  147. max = max<N ? N : max;
  148. //printf("sending info to '%s'\n", (path+to_s(N)).c_str());
  149. uToB->write((path+to_s(N)).c_str(), "ifb",
  150. s.size, s.basefreq, sizeof(float*), &s.smp);
  151. }, []{return false;});
  152. //clear out unused samples
  153. for(unsigned i = max+1; i < PAD_MAX_SAMPLES; ++i) {
  154. uToB->write((path+to_s(i)).c_str(), "ifb",
  155. 0, 440.0f, sizeof(float*), NULL);
  156. }
  157. }
  158. /*****************************************************************************
  159. * Instrument Banks *
  160. * *
  161. * Banks and presets in general are not classed as realtime safe *
  162. * *
  163. * The supported operations are: *
  164. * - Load Names *
  165. * - Load Bank *
  166. * - Refresh List of Banks *
  167. *****************************************************************************/
  168. void refreshBankView(const Bank &bank, unsigned loc, Fl_Osc_Interface *osc)
  169. {
  170. if(loc >= BANK_SIZE)
  171. return;
  172. char response[2048];
  173. if(!rtosc_message(response, 1024, "/bankview", "iss",
  174. loc, bank.ins[loc].name.c_str(),
  175. bank.ins[loc].filename.c_str()))
  176. errx(1, "Failure to handle bank update properly...");
  177. if (osc)
  178. osc->tryLink(response);
  179. }
  180. void bankList(Bank &bank, Fl_Osc_Interface *osc)
  181. {
  182. if (! osc)
  183. return;
  184. char response[2048];
  185. int i = 0;
  186. for(auto &elm : bank.banks) {
  187. if(!rtosc_message(response, 2048, "/bank-list", "iss",
  188. i++, elm.name.c_str(), elm.dir.c_str()))
  189. errx(1, "Failure to handle bank update properly...");
  190. if (osc)
  191. osc->tryLink(response);
  192. }
  193. }
  194. void rescanForBanks(Bank &bank, Fl_Osc_Interface *osc)
  195. {
  196. bank.rescanforbanks();
  197. bankList(bank, osc);
  198. }
  199. void loadBank(Bank &bank, int pos, Fl_Osc_Interface *osc)
  200. {
  201. if(bank.bankpos != pos) {
  202. bank.bankpos = pos;
  203. bank.loadbank(bank.banks[pos].dir);
  204. for(int i=0; i<BANK_SIZE; ++i)
  205. refreshBankView(bank, i, osc);
  206. }
  207. }
  208. void bankPos(Bank &bank, Fl_Osc_Interface *osc)
  209. {
  210. char response[2048];
  211. if(!rtosc_message(response, 2048, "/loadbank", "i", bank.bankpos))
  212. errx(1, "Failure to handle bank update properly...");
  213. if (osc)
  214. osc->tryLink(response);
  215. }
  216. /*****************************************************************************
  217. * Data Object for Non-RT Class Dispatch *
  218. *****************************************************************************/
  219. class DummyDataObj:public rtosc::RtData
  220. {
  221. public:
  222. DummyDataObj(char *loc_, size_t loc_size_, void *obj_, MiddleWareImpl *mwi_,
  223. rtosc::ThreadLink *uToB_)
  224. {
  225. memset(loc_, 0, sizeof(loc_size_));
  226. buffer = new char[4*4096];
  227. memset(buffer, 0, 4*4096);
  228. loc = loc_;
  229. loc_size = loc_size_;
  230. obj = obj_;
  231. mwi = mwi_;
  232. uToB = uToB_;
  233. }
  234. ~DummyDataObj(void)
  235. {
  236. delete[] buffer;
  237. }
  238. virtual void reply(const char *path, const char *args, ...)
  239. {
  240. //printf("reply building '%s'\n", path);
  241. va_list va;
  242. va_start(va,args);
  243. if(!strcmp(path, "/forward")) { //forward the information to the backend
  244. args++;
  245. path = va_arg(va, const char *);
  246. //fprintf(stderr, "forwarding information to the backend on '%s'<%s>\n",
  247. // path, args);
  248. rtosc_vmessage(buffer,4*4096,path,args,va);
  249. uToB->raw_write(buffer);
  250. } else {
  251. //printf("path = '%s' args = '%s'\n", path, args);
  252. //printf("buffer = '%p'\n", buffer);
  253. rtosc_vmessage(buffer,4*4096,path,args,va);
  254. //printf("buffer = '%s'\n", buffer);
  255. reply(buffer);
  256. }
  257. va_end(va);
  258. }
  259. virtual void reply(const char *msg);
  260. //virtual void broadcast(const char *path, const char *args, ...){(void)path;(void)args;};
  261. //virtual void broadcast(const char *msg){(void)msg;};
  262. private:
  263. char *buffer;
  264. MiddleWareImpl *mwi;
  265. rtosc::ThreadLink *uToB;
  266. };
  267. /******************************************************************************
  268. * Non-RealTime Object Store *
  269. * *
  270. * *
  271. * Storage For Objects which need to be interfaced with outside the realtime *
  272. * thread (aka they have long lived operations which can be done out-of-band) *
  273. * *
  274. * - OscilGen instances as prepare() cannot be done in realtime and PAD *
  275. * depends on these instances *
  276. * - PADnoteParameter instances as applyparameters() cannot be done in *
  277. * realtime *
  278. * *
  279. * These instances are collected on every part change and kit change *
  280. ******************************************************************************/
  281. struct NonRtObjStore
  282. {
  283. std::map<std::string, void*> objmap;
  284. void extractMaster(Master *master)
  285. {
  286. for(int i=0; i < NUM_MIDI_PARTS; ++i) {
  287. extractPart(master->part[i], i);
  288. }
  289. }
  290. void extractPart(Part *part, int i)
  291. {
  292. for(int j=0; j < NUM_KIT_ITEMS; ++j) {
  293. auto &obj = part->kit[j];
  294. extractAD(obj.adpars, i, j);
  295. extractPAD(obj.padpars, i, j);
  296. }
  297. }
  298. void extractAD(ADnoteParameters *adpars, int i, int j)
  299. {
  300. std::string base = "/part"+to_s(i)+"/kit"+to_s(j)+"/";
  301. for(int k=0; k<NUM_VOICES; ++k) {
  302. std::string nbase = base+"adpars/voice"+to_s(k)+"/";
  303. if(adpars) {
  304. auto &nobj = adpars->VoicePar[k];
  305. objmap[nbase+"oscil/"] = nobj.OscilSmp;
  306. objmap[nbase+"mod-oscil/"] = nobj.FMSmp;
  307. } else {
  308. objmap[nbase+"oscil/"] = nullptr;
  309. objmap[nbase+"mod-oscil/"] = nullptr;
  310. }
  311. }
  312. }
  313. void extractPAD(PADnoteParameters *padpars, int i, int j)
  314. {
  315. std::string base = "/part"+to_s(i)+"/kit"+to_s(j)+"/";
  316. for(int k=0; k<NUM_VOICES; ++k) {
  317. if(padpars) {
  318. objmap[base+"padpars/"] = padpars;
  319. objmap[base+"padpars/oscil/"] = padpars->oscilgen;
  320. } else {
  321. objmap[base+"padpars/"] = nullptr;
  322. objmap[base+"padpars/oscil/"] = nullptr;
  323. }
  324. }
  325. }
  326. void clear(void)
  327. {
  328. objmap.clear();
  329. }
  330. bool has(std::string loc)
  331. {
  332. return objmap.find(loc) != objmap.end();
  333. }
  334. void *get(std::string loc)
  335. {
  336. return objmap[loc];
  337. }
  338. };
  339. /******************************************************************************
  340. * Realtime Parameter Store *
  341. * *
  342. * Storage for AD/PAD/SUB parameters which are allocated as needed by kits. *
  343. * Two classes of events affect this: *
  344. * 1. When a message to enable a kit is observed, then the kit is allocated *
  345. * and sent prior to the enable message. *
  346. * 2. When a part is allocated all part information is rebuilt *
  347. * *
  348. * (NOTE pointers aren't really needed here, just booleans on whether it has *
  349. * been allocated) *
  350. * This may be later utilized for copy/paste support *
  351. ******************************************************************************/
  352. struct ParamStore
  353. {
  354. ParamStore(void)
  355. {
  356. memset(add, 0, sizeof(add));
  357. memset(pad, 0, sizeof(pad));
  358. memset(sub, 0, sizeof(sub));
  359. }
  360. void extractPart(Part *part, int i)
  361. {
  362. for(int j=0; j < NUM_KIT_ITEMS; ++j) {
  363. auto kit = part->kit[j];
  364. add[i][j] = kit.adpars;
  365. sub[i][j] = kit.subpars;
  366. pad[i][j] = kit.padpars;
  367. }
  368. }
  369. ADnoteParameters *add[NUM_MIDI_PARTS][NUM_KIT_ITEMS];
  370. SUBnoteParameters *sub[NUM_MIDI_PARTS][NUM_KIT_ITEMS];
  371. PADnoteParameters *pad[NUM_MIDI_PARTS][NUM_KIT_ITEMS];
  372. };
  373. /* Implementation */
  374. class MiddleWareImpl
  375. {
  376. static constexpr const char* tmp_nam_prefix = "/tmp/zynaddsubfx_";
  377. MiddleWare *parent;
  378. //! returns file name to where UDP port is saved
  379. std::string get_tmp_nam() const
  380. {
  381. return tmp_nam_prefix + to_s(getpid());
  382. }
  383. void create_tmp_file(unsigned server_port)
  384. {
  385. std::string tmp_nam = get_tmp_nam();
  386. if(0 == access(tmp_nam.c_str(), F_OK)) {
  387. fprintf(stderr, "Error: Cannot overwrite file %s. "
  388. "You should probably remove it.", tmp_nam.c_str());
  389. exit(EXIT_FAILURE);
  390. }
  391. FILE* tmp_fp = fopen(tmp_nam.c_str(), "w");
  392. if(!tmp_fp)
  393. fprintf(stderr, "Warning: could not create new file %s.\n",
  394. tmp_nam.c_str());
  395. else
  396. fprintf(tmp_fp, "%u", server_port);
  397. fclose(tmp_fp);
  398. }
  399. void clean_up_tmp_nams() const
  400. {
  401. DIR *dir;
  402. struct dirent *entry;
  403. if ((dir = opendir ("/tmp/")) != nullptr)
  404. {
  405. while ((entry = readdir (dir)) != nullptr)
  406. {
  407. std::string name = std::string("/tmp/") + entry->d_name;
  408. if(!name.compare(0, strlen(tmp_nam_prefix),tmp_nam_prefix))
  409. {
  410. std::string pid = name.substr(strlen(tmp_nam_prefix));
  411. std::string proc_file = "/proc/" + std::move(pid) +
  412. "/comm";
  413. std::ifstream ifs(proc_file);
  414. bool remove = false;
  415. if(!ifs.good())
  416. {
  417. fprintf(stderr, "Note: trying to remove %s - the "
  418. "process does not exist anymore.\n",
  419. name.c_str());
  420. remove = true;
  421. }
  422. else
  423. {
  424. std::string comm_name;
  425. ifs >> comm_name;
  426. if(comm_name == "zynaddsubfx")
  427. fprintf(stderr, "Note: detected running "
  428. "zynaddsubfx with PID %s.\n",
  429. name.c_str() + strlen(tmp_nam_prefix));
  430. else {
  431. fprintf(stderr, "Note: trying to remove %s - the "
  432. "PID is owned by\n another "
  433. "process: %s\n",
  434. name.c_str(), comm_name.c_str());
  435. remove = true;
  436. }
  437. }
  438. if(remove)
  439. {
  440. // make sure this file contains only one unsigned
  441. unsigned udp_port;
  442. std::ifstream ifs2(name);
  443. if(!ifs2.good())
  444. fprintf(stderr, "Warning: could not open %s.\n",
  445. name.c_str());
  446. else
  447. {
  448. ifs2 >> udp_port;
  449. if(ifs.good())
  450. fprintf(stderr, "Warning: could not remove "
  451. "%s, \n it has not been "
  452. "written by zynaddsubfx\n",
  453. name.c_str());
  454. else
  455. {
  456. if(std::remove(name.c_str()) != 0)
  457. fprintf(stderr, "Warning: can not remove "
  458. "%s.\n", name.c_str());
  459. }
  460. }
  461. }
  462. /* one might want to connect to zyn here,
  463. but it is not necessary:
  464. lo_address la = lo_address_new(nullptr, udp_port.c_str());
  465. if(lo_send(la, "/echo", nullptr) <= 0)
  466. fputs("Note: found crashed file %s\n", stderr);
  467. lo_address_free(la);*/
  468. }
  469. }
  470. closedir (dir);
  471. } else {
  472. fputs("Warning: can not read /tmp.\n", stderr);
  473. }
  474. }
  475. public:
  476. MiddleWareImpl(MiddleWare *mw, SYNTH_T synth, int prefered_port);
  477. ~MiddleWareImpl(void);
  478. void warnMemoryLeaks(void);
  479. //Apply function while parameters are write locked
  480. void doReadOnlyOp(std::function<void()> read_only_fn);
  481. void saveBankSlot(int npart, int nslot, Master *master, Fl_Osc_Interface *osc)
  482. {
  483. int err = 0;
  484. doReadOnlyOp([master,nslot,npart,&err](){
  485. err = master->bank.savetoslot(nslot, master->part[npart]);});
  486. if(err) {
  487. char buffer[1024];
  488. rtosc_message(buffer, 1024, "/alert", "s",
  489. "Failed To Save To Bank Slot, please check file permissions");
  490. GUI::raiseUi(ui, buffer);
  491. }
  492. }
  493. void renameBankSlot(int slot, string name, Master *master, Fl_Osc_Interface *osc)
  494. {
  495. int err = master->bank.setname(slot, name, -1);
  496. if(err) {
  497. char buffer[1024];
  498. rtosc_message(buffer, 1024, "/alert", "s",
  499. "Failed To Rename Bank Slot, please check file permissions");
  500. GUI::raiseUi(ui, buffer);
  501. }
  502. }
  503. void swapBankSlot(int slota, int slotb, Master *master, Fl_Osc_Interface *osc)
  504. {
  505. int err = master->bank.swapslot(slota, slotb);
  506. if(err) {
  507. char buffer[1024];
  508. rtosc_message(buffer, 1024, "/alert", "s",
  509. "Failed To Swap Bank Slots, please check file permissions");
  510. GUI::raiseUi(ui, buffer);
  511. }
  512. }
  513. void clearBankSlot(int slot, Master *master, Fl_Osc_Interface *osc)
  514. {
  515. int err = master->bank.clearslot(slot);
  516. if(err) {
  517. char buffer[1024];
  518. rtosc_message(buffer, 1024, "/alert", "s",
  519. "Failed To Clear Bank Slot, please check file permissions");
  520. GUI::raiseUi(ui, buffer);
  521. }
  522. }
  523. void saveMaster(const char *filename)
  524. {
  525. //Copy is needed as filename WILL get trashed during the rest of the run
  526. std::string fname = filename;
  527. //printf("saving master('%s')\n", filename);
  528. doReadOnlyOp([this,fname](){
  529. int res = master->saveXML(fname.c_str());
  530. (void)res;
  531. /*printf("results: '%s' '%d'\n",fname.c_str(), res);*/});
  532. }
  533. void savePart(int npart, const char *filename)
  534. {
  535. //Copy is needed as filename WILL get trashed during the rest of the run
  536. std::string fname = filename;
  537. //printf("saving part(%d,'%s')\n", npart, filename);
  538. doReadOnlyOp([this,fname,npart](){
  539. int res = master->part[npart]->saveXML(fname.c_str());
  540. (void)res;
  541. /*printf("results: '%s' '%d'\n",fname.c_str(), res);*/});
  542. }
  543. void loadPart(int npart, const char *filename, Master *master, Fl_Osc_Interface *osc)
  544. {
  545. actual_load[npart]++;
  546. if(actual_load[npart] != pending_load[npart])
  547. return;
  548. assert(actual_load[npart] <= pending_load[npart]);
  549. auto alloc = std::async(std::launch::async,
  550. [master,filename,this,npart](){
  551. Part *p = new Part(*master->memory, synth, &master->microtonal, master->fft);
  552. if(p->loadXMLinstrument(filename))
  553. fprintf(stderr, "Warning: failed to load part!\n");
  554. auto isLateLoad = [this,npart]{
  555. return actual_load[npart] != pending_load[npart];
  556. };
  557. p->applyparameters(isLateLoad);
  558. return p;});
  559. //Load the part
  560. if(idle) {
  561. while(alloc.wait_for(std::chrono::seconds(0)) != std::future_status::ready) {
  562. idle(idle_ptr);
  563. }
  564. }
  565. Part *p = alloc.get();
  566. obj_store.extractPart(p, npart);
  567. kits.extractPart(p, npart);
  568. //Give it to the backend and wait for the old part to return for
  569. //deallocation
  570. uToB->write("/load-part", "ib", npart, sizeof(Part*), &p);
  571. GUI::raiseUi(ui, "/damage", "s", ("/part"+to_s(npart)+"/").c_str());
  572. }
  573. //Load a new cleared Part instance
  574. void loadClearPart(int npart)
  575. {
  576. if(npart == -1)
  577. return;
  578. Part *p = new Part(*master->memory, synth, &master->microtonal, master->fft);
  579. p->applyparameters();
  580. obj_store.extractPart(p, npart);
  581. kits.extractPart(p, npart);
  582. //Give it to the backend and wait for the old part to return for
  583. //deallocation
  584. uToB->write("/load-part", "ib", npart, sizeof(Part*), &p);
  585. GUI::raiseUi(ui, "/damage", "s", ("/part"+to_s(npart)+"/").c_str());
  586. //if(osc)
  587. // osc->damage(("/part"+to_s(npart)+"/").c_str());
  588. }
  589. //Well, you don't get much crazier than changing out all of your RT
  590. //structures at once... TODO error handling
  591. void loadMaster(const char *filename)
  592. {
  593. Master *m = new Master(synth);
  594. m->uToB = uToB;
  595. m->bToU = bToU;
  596. if(filename) {
  597. m->loadXML(filename);
  598. m->applyparameters();
  599. }
  600. //Update resource locator table
  601. updateResources(m);
  602. master = m;
  603. //Give it to the backend and wait for the old part to return for
  604. //deallocation
  605. uToB->write("/load-master", "b", sizeof(Master*), &m);
  606. }
  607. void updateResources(Master *m)
  608. {
  609. obj_store.clear();
  610. obj_store.extractMaster(m);
  611. for(int i=0; i<NUM_MIDI_PARTS; ++i)
  612. kits.extractPart(m->part[i], i);
  613. }
  614. //If currently broadcasting messages
  615. bool broadcast = false;
  616. //If accepting undo events as user driven
  617. bool recording_undo = true;
  618. void bToUhandle(const char *rtmsg, bool dummy=false);
  619. void tick(void)
  620. {
  621. while(lo_server_recv_noblock(server, 0));
  622. while(bToU->hasNext()) {
  623. const char *rtmsg = bToU->read();
  624. bToUhandle(rtmsg);
  625. }
  626. }
  627. bool handlePAD(string path, const char *msg, void *v)
  628. {
  629. if(!v)
  630. return true;
  631. char buffer[1024];
  632. memset(buffer, 0, sizeof(buffer));
  633. DummyDataObj d(buffer, 1024, v, this, uToB);
  634. strcpy(buffer, path.c_str());
  635. PADnoteParameters::ports.dispatch(msg, d);
  636. if(!d.matches) {
  637. fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 1, 7 + 30, 0 + 40);
  638. fprintf(stderr, "Unknown location '%s%s'<%s>\n",
  639. path.c_str(), msg, rtosc_argument_string(msg));
  640. fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  641. }
  642. return true;
  643. }
  644. void handlePresets(const char *msg)
  645. {
  646. char buffer[1024];
  647. memset(buffer, 0, sizeof(buffer));
  648. DummyDataObj d(buffer, 1024, (void*)parent, this, uToB);
  649. strcpy(buffer, "/presets/");
  650. //012345678
  651. ///presets/
  652. real_preset_ports.dispatch(msg+9, d);
  653. //printf("Something <%s>\n", msg+9);
  654. if(strstr(msg, "paste") && rtosc_argument_string(msg)[0] == 's') {
  655. char buffer[1024];
  656. rtosc_message(buffer, 1024, "/damage", "s",
  657. rtosc_argument(msg, 0).s);
  658. GUI::raiseUi(ui, buffer);
  659. }
  660. if(!d.matches) {
  661. fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 1, 7 + 30, 0 + 40);
  662. fprintf(stderr, "Unknown location '%s'<%s>\n",
  663. msg, rtosc_argument_string(msg));
  664. fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  665. }
  666. }
  667. void handleConfig(const char *msg)
  668. {
  669. char buffer[1024];
  670. memset(buffer, 0, sizeof(buffer));
  671. DummyDataObj d(buffer, 1024, (void*)&config, this, uToB);
  672. strcpy(buffer, "/config/");
  673. Config::ports.dispatch(msg+8, d);
  674. if(!d.matches) {
  675. fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 1, 7 + 30, 0 + 40);
  676. fprintf(stderr, "Unknown location '%s'<%s>\n",
  677. msg, rtosc_argument_string(msg));
  678. fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  679. }
  680. }
  681. bool handleOscil(string path, const char *msg, void *v);
  682. void kitEnable(const char *msg);
  683. void kitEnable(int part, int kit, int type);
  684. // Handle an event with special cases
  685. void handleMsg(const char *msg);
  686. void write(const char *path, const char *args, ...);
  687. void write(const char *path, const char *args, va_list va);
  688. /*
  689. * Provides a mapping for non-RT objects stored inside the backend
  690. * - Oscilgen almost all parameters can be safely set
  691. * - Padnote can have anything set on its oscilgen and a very small set
  692. * of general parameters
  693. */
  694. NonRtObjStore obj_store;
  695. //This code will own the pointer to master, be prepared for odd things if
  696. //this assumption is broken
  697. Master *master;
  698. //The ONLY means that any chunk of UI code should have for interacting with the
  699. //backend
  700. Fl_Osc_Interface *osc;
  701. //Synth Engine Parameters
  702. ParamStore kits;
  703. //Callback When Waiting on async events
  704. void(*idle)(void*);
  705. void* idle_ptr;
  706. //General UI callback
  707. cb_t cb;
  708. //UI handle
  709. void *ui;
  710. std::atomic_int pending_load[NUM_MIDI_PARTS];
  711. std::atomic_int actual_load[NUM_MIDI_PARTS];
  712. rtosc::UndoHistory undo;
  713. //Link To the Realtime
  714. rtosc::ThreadLink *bToU;
  715. rtosc::ThreadLink *uToB;
  716. //LIBLO
  717. lo_server server;
  718. string last_url, curr_url;
  719. //Synthesis Rate Parameters
  720. const SYNTH_T synth;
  721. };
  722. MiddleWareImpl::MiddleWareImpl(MiddleWare *mw, SYNTH_T synth_, int prefered_port)
  723. :parent(mw), synth(synth_)
  724. {
  725. bToU = new rtosc::ThreadLink(4096*2,1024);
  726. uToB = new rtosc::ThreadLink(4096*2,1024);
  727. if(prefered_port != -1)
  728. server = lo_server_new_with_proto(to_s(prefered_port).c_str(), LO_UDP, liblo_error_cb);
  729. else
  730. server = lo_server_new_with_proto(NULL, LO_UDP, liblo_error_cb);
  731. lo_server_add_method(server, NULL, NULL, handler_function, mw);
  732. fprintf(stderr, "lo server running on %d\n", lo_server_get_port(server));
  733. #ifndef PLUGINVERSION
  734. clean_up_tmp_nams();
  735. create_tmp_file((unsigned)lo_server_get_port(server));
  736. #endif
  737. //dummy callback for starters
  738. cb = [](void*, const char*){};
  739. idle = 0;
  740. idle_ptr = 0;
  741. #ifndef PLUGINVERSION
  742. the_bToU = bToU;
  743. #endif
  744. master = new Master(synth);
  745. master->bToU = bToU;
  746. master->uToB = uToB;
  747. osc = GUI::genOscInterface(mw);
  748. //Grab objects of interest from master
  749. updateResources(master);
  750. //Null out Load IDs
  751. for(int i=0; i < NUM_MIDI_PARTS; ++i) {
  752. pending_load[i] = 0;
  753. actual_load[i] = 0;
  754. }
  755. //Setup Undo
  756. undo.setCallback([this](const char *msg) {
  757. // printf("undo callback <%s>\n", msg);
  758. char buf[1024];
  759. rtosc_message(buf, 1024, "/undo_pause","");
  760. handleMsg(buf);
  761. handleMsg(msg);
  762. rtosc_message(buf, 1024, "/undo_resume","");
  763. handleMsg(buf);
  764. });
  765. }
  766. void DummyDataObj::reply(const char *msg)
  767. {
  768. mwi->bToUhandle(msg, true);
  769. }
  770. MiddleWareImpl::~MiddleWareImpl(void)
  771. {
  772. remove(get_tmp_nam().c_str());
  773. warnMemoryLeaks();
  774. delete master;
  775. delete osc;
  776. delete bToU;
  777. delete uToB;
  778. }
  779. /** Threading When Saving
  780. * ----------------------
  781. *
  782. * Procedure Middleware:
  783. * 1) Middleware sends /freeze_state to backend
  784. * 2) Middleware waits on /state_frozen from backend
  785. * All intervening commands are held for out of order execution
  786. * 3) Aquire memory
  787. * At this time by the memory barrier we are guarenteed that all old
  788. * writes are done and assuming the freezing logic is sound, then it is
  789. * impossible for any other parameter to change at this time
  790. * 3) Middleware performs saving operation
  791. * 4) Middleware sends /thaw_state to backend
  792. * 5) Restore in order execution
  793. *
  794. * Procedure Backend:
  795. * 1) Observe /freeze_state and disable all mutating events (MIDI CC)
  796. * 2) Run a memory release to ensure that all writes are complete
  797. * 3) Send /state_frozen to Middleware
  798. * time...
  799. * 4) Observe /thaw_state and resume normal processing
  800. */
  801. void MiddleWareImpl::doReadOnlyOp(std::function<void()> read_only_fn)
  802. {
  803. uToB->write("/freeze_state","");
  804. std::list<const char *> fico;
  805. int tries = 0;
  806. while(tries++ < 10000) {
  807. if(!bToU->hasNext()) {
  808. usleep(500);
  809. continue;
  810. }
  811. const char *msg = bToU->read();
  812. if(!strcmp("/state_frozen", msg))
  813. break;
  814. size_t bytes = rtosc_message_length(msg, bToU->buffer_size());
  815. char *save_buf = new char[bytes];
  816. memcpy(save_buf, msg, bytes);
  817. fico.push_back(save_buf);
  818. }
  819. assert(tries < 10000);//if this happens, the backend must be dead
  820. std::atomic_thread_fence(std::memory_order_acquire);
  821. //Now it is safe to do any read only operation
  822. read_only_fn();
  823. //Now to resume normal operations
  824. uToB->write("/thaw_state","");
  825. for(auto x:fico) {
  826. uToB->raw_write(x);
  827. delete [] x;
  828. }
  829. }
  830. void MiddleWareImpl::bToUhandle(const char *rtmsg, bool dummy)
  831. {
  832. assert(strcmp(rtmsg, "/part0/kit0/Ppadenableda"));
  833. assert(strcmp(rtmsg, "/ze_state"));
  834. //Dump Incomming Events For Debugging
  835. if(strcmp(rtmsg, "/vu-meter") && false) {
  836. fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 1 + 30, 0 + 40);
  837. fprintf(stdout, "frontend: '%s'<%s>\n", rtmsg,
  838. rtosc_argument_string(rtmsg));
  839. fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  840. }
  841. //Activity dot
  842. //printf(".");fflush(stdout);
  843. if(!strcmp(rtmsg, "/echo")
  844. && !strcmp(rtosc_argument_string(rtmsg),"ss")
  845. && !strcmp(rtosc_argument(rtmsg,0).s, "OSC_URL"))
  846. curr_url = rtosc_argument(rtmsg,1).s;
  847. else if(!strcmp(rtmsg, "/free")
  848. && !strcmp(rtosc_argument_string(rtmsg),"sb")) {
  849. deallocate(rtosc_argument(rtmsg, 0).s, *((void**)rtosc_argument(rtmsg, 1).b.data));
  850. } else if(!strcmp(rtmsg, "/request-memory")) {
  851. //Generate out more memory for the RT memory pool
  852. //5MBi chunk
  853. size_t N = 5*1024*1024;
  854. void *mem = malloc(N);
  855. uToB->write("/add-rt-memory", "bi", sizeof(void*), &mem, N);
  856. } else if(!strcmp(rtmsg, "/setprogram")
  857. && !strcmp(rtosc_argument_string(rtmsg),"cc")) {
  858. loadPart(rtosc_argument(rtmsg,0).i, master->bank.ins[rtosc_argument(rtmsg,1).i].filename.c_str(), master, osc);
  859. } else if(!strcmp("/undo_pause", rtmsg)) {
  860. recording_undo = false;
  861. } else if(!strcmp("/undo_resume", rtmsg)) {
  862. recording_undo = true;
  863. } else if(!strcmp("undo_change", rtmsg) && recording_undo) {
  864. undo.recordEvent(rtmsg);
  865. } else if(!strcmp(rtmsg, "/broadcast")) {
  866. broadcast = true;
  867. } else if(broadcast) {
  868. broadcast = false;
  869. #ifdef PLUGINVERSION
  870. if (!curr_url.empty()) // falktx: check added
  871. cb(ui, rtmsg);
  872. // falktx: changed curr_url to last_url
  873. if(last_url != "GUI") {
  874. lo_message msg = lo_message_deserialise((void*)rtmsg,
  875. rtosc_message_length(rtmsg, bToU->buffer_size()), NULL);
  876. //Send to known url
  877. if(!last_url.empty()) {
  878. lo_address addr = lo_address_new_from_url(last_url.c_str());
  879. lo_send_message(addr, rtmsg, msg);
  880. }
  881. }
  882. #else
  883. cb(ui, rtmsg);
  884. if(curr_url != "GUI") {
  885. lo_message msg = lo_message_deserialise((void*)rtmsg,
  886. rtosc_message_length(rtmsg, bToU->buffer_size()), NULL);
  887. //Send to known url
  888. if(!curr_url.empty()) {
  889. lo_address addr = lo_address_new_from_url(curr_url.c_str());
  890. lo_send_message(addr, rtmsg, msg);
  891. }
  892. }
  893. #endif
  894. } else if((dummy?last_url:curr_url) == "GUI" || !strcmp(rtmsg, "/close-ui")) {
  895. cb(ui, rtmsg);
  896. } else{
  897. lo_message msg = lo_message_deserialise((void*)rtmsg,
  898. rtosc_message_length(rtmsg, bToU->buffer_size()), NULL);
  899. //Send to known url
  900. if(!curr_url.empty()) {
  901. lo_address addr = lo_address_new_from_url(dummy?last_url.c_str():curr_url.c_str());
  902. lo_send_message(addr, rtmsg, msg);
  903. }
  904. }
  905. }
  906. bool MiddleWareImpl::handleOscil(string path, const char *msg, void *v)
  907. {
  908. //printf("handleOscil...\n");
  909. char buffer[1024];
  910. memset(buffer, 0, sizeof(buffer));
  911. DummyDataObj d(buffer, 1024, v, this, uToB);
  912. strcpy(buffer, path.c_str());
  913. if(!v)
  914. return true;
  915. //Paste To Non-Realtime Parameters and then forward
  916. if(strstr(msg, "paste") && !strstr(msg, "padpars")) {
  917. }
  918. if(!strstr(msg, "padpars")) {
  919. for(auto &p:OscilGen::ports.ports) {
  920. if(strstr(p.name,msg) && strstr(p.metadata, "realtime") &&
  921. !strcmp("b", rtosc_argument_string(msg))) {
  922. //printf("sending along packet '%s'...\n", msg);
  923. return false;
  924. }
  925. }
  926. }
  927. OscilGen::ports.dispatch(msg, d);
  928. if(!d.matches) {
  929. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 1, 7 + 30, 0 + 40);
  930. //fprintf(stderr, "Unknown location '%s%s'<%s>\n",
  931. // path.c_str(), msg, rtosc_argument_string(msg));
  932. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  933. }
  934. return true;
  935. }
  936. //Allocate kits on a as needed basis
  937. void MiddleWareImpl::kitEnable(const char *msg)
  938. {
  939. const string argv = rtosc_argument_string(msg);
  940. if(argv != "T")
  941. return;
  942. //Extract fields from:
  943. //BASE/part#/kit#/Pxxxenabled
  944. int type = -1;
  945. if(strstr(msg, "Padenabled"))
  946. type = 0;
  947. else if(strstr(msg, "Ppadenabled"))
  948. type = 1;
  949. else if(strstr(msg, "Psubenabled"))
  950. type = 2;
  951. if(type == -1)
  952. return;
  953. const char *tmp = strstr(msg, "part");
  954. if(tmp == NULL)
  955. return;
  956. const int part = atoi(tmp+4);
  957. tmp = strstr(msg, "kit");
  958. if(tmp == NULL)
  959. return;
  960. const int kit = atoi(tmp+3);
  961. kitEnable(part, kit, type);
  962. }
  963. void MiddleWareImpl::kitEnable(int part, int kit, int type)
  964. {
  965. //printf("attempting a kit enable<%d,%d,%d>\n", part, kit, type);
  966. string url = "/part"+to_s(part)+"/kit"+to_s(kit)+"/";
  967. void *ptr = NULL;
  968. if(type == 0 && kits.add[part][kit] == NULL) {
  969. ptr = kits.add[part][kit] = new ADnoteParameters(synth, master->fft);
  970. url += "adpars-data";
  971. obj_store.extractAD(kits.add[part][kit], part, kit);
  972. } else if(type == 1 && kits.pad[part][kit] == NULL) {
  973. ptr = kits.pad[part][kit] = new PADnoteParameters(synth, master->fft);
  974. url += "padpars-data";
  975. obj_store.extractPAD(kits.pad[part][kit], part, kit);
  976. } else if(type == 2 && kits.sub[part][kit] == NULL) {
  977. ptr = kits.sub[part][kit] = new SUBnoteParameters();
  978. url += "subpars-data";
  979. }
  980. //Send the new memory
  981. if(ptr)
  982. uToB->write(url.c_str(), "b", sizeof(void*), &ptr);
  983. }
  984. /* BASE/part#/kititem#
  985. * BASE/part#/kit#/adpars/voice#/oscil/\*
  986. * BASE/part#/kit#/adpars/voice#/mod-oscil/\*
  987. * BASE/part#/kit#/padpars/prepare
  988. * BASE/part#/kit#/padpars/oscil/\*
  989. */
  990. void MiddleWareImpl::handleMsg(const char *msg)
  991. {
  992. assert(!strstr(msg,"free"));
  993. assert(msg && *msg && rindex(msg, '/')[1]);
  994. assert(strcmp(msg, "/part0/Psysefxvol"));
  995. assert(strcmp(msg, "/Penabled"));
  996. assert(strcmp(msg, "part0/part0/Ppanning"));
  997. assert(strcmp(msg, "sysefx0sysefx0/preset"));
  998. assert(strcmp(msg, "/sysefx0preset"));
  999. assert(strcmp(msg, "Psysefxvol0/part0"));
  1000. //fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 6 + 30, 0 + 40);
  1001. //fprintf(stdout, "middleware: '%s':%s\n", msg, rtosc_argument_string(msg));
  1002. //fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  1003. const char *last_path = rindex(msg, '/');
  1004. if(!last_path)
  1005. return;
  1006. //printf("watching '%s' go by\n", msg);
  1007. //Get the object resource locator
  1008. string obj_rl(msg, last_path+1);
  1009. int npart = -1;
  1010. char testchr = 0;
  1011. if(!strcmp(msg, "/refresh_bank") && !strcmp(rtosc_argument_string(msg), "i")) {
  1012. refreshBankView(master->bank, rtosc_argument(msg,0).i, osc);
  1013. } else if(!strcmp(msg, "/bank-list") && !strcmp(rtosc_argument_string(msg), "")) {
  1014. bankList(master->bank, osc);
  1015. } else if(!strcmp(msg, "/rescanforbanks") && !strcmp(rtosc_argument_string(msg), "")) {
  1016. rescanForBanks(master->bank, osc);
  1017. } else if(!strcmp(msg, "/loadbank") && !strcmp(rtosc_argument_string(msg), "i")) {
  1018. loadBank(master->bank, rtosc_argument(msg, 0).i, osc);
  1019. } else if(!strcmp(msg, "/loadbank") && !strcmp(rtosc_argument_string(msg), "")) {
  1020. bankPos(master->bank, osc);
  1021. } else if(obj_store.has(obj_rl)) {
  1022. //try some over simplified pattern matching
  1023. if(strstr(msg, "oscilgen/") || strstr(msg, "FMSmp/") || strstr(msg, "OscilSmp/")) {
  1024. if(!handleOscil(obj_rl, last_path+1, obj_store.get(obj_rl)))
  1025. uToB->raw_write(msg);
  1026. //else if(strstr(obj_rl.c_str(), "kititem"))
  1027. // handleKitItem(obj_rl, objmap[obj_rl],atoi(rindex(msg,'m')+1),rtosc_argument(msg,0).T);
  1028. } else if(strstr(msg, "padpars/prepare"))
  1029. preparePadSynth(obj_rl,(PADnoteParameters *) obj_store.get(obj_rl), uToB);
  1030. else if(strstr(msg, "padpars")) {
  1031. if(!handlePAD(obj_rl, last_path+1, obj_store.get(obj_rl)))
  1032. uToB->raw_write(msg);
  1033. } else //just forward the message
  1034. uToB->raw_write(msg);
  1035. } else if(strstr(msg, "/save_xmz") && !strcmp(rtosc_argument_string(msg), "s")) {
  1036. saveMaster(rtosc_argument(msg,0).s);
  1037. } else if(strstr(msg, "/save_xiz") && !strcmp(rtosc_argument_string(msg), "is")) {
  1038. savePart(rtosc_argument(msg,0).i,rtosc_argument(msg,1).s);
  1039. } else if(strstr(msg, "/load_xmz") && !strcmp(rtosc_argument_string(msg), "s")) {
  1040. loadMaster(rtosc_argument(msg,0).s);
  1041. } else if(strstr(msg, "/reset_master") && !strcmp(rtosc_argument_string(msg), "")) {
  1042. loadMaster(NULL);
  1043. } else if(!strcmp(msg, "/load_xiz") && !strcmp(rtosc_argument_string(msg), "is")) {
  1044. pending_load[rtosc_argument(msg,0).i]++;
  1045. loadPart(rtosc_argument(msg,0).i, rtosc_argument(msg,1).s, master, osc);
  1046. } else if(strstr(msg, "load-part") && !strcmp(rtosc_argument_string(msg), "is")) {
  1047. pending_load[rtosc_argument(msg,0).i]++;
  1048. loadPart(rtosc_argument(msg,0).i, rtosc_argument(msg,1).s, master, osc);
  1049. } else if(!strcmp(msg, "/setprogram")
  1050. && !strcmp(rtosc_argument_string(msg),"c")) {
  1051. pending_load[0]++;
  1052. loadPart(0, master->bank.ins[rtosc_argument(msg,0).i].filename.c_str(), master, osc);
  1053. } else if(strstr(msg, "save-bank-part") && !strcmp(rtosc_argument_string(msg), "ii")) {
  1054. saveBankSlot(rtosc_argument(msg,0).i, rtosc_argument(msg,1).i, master, osc);
  1055. } else if(strstr(msg, "bank-rename") && !strcmp(rtosc_argument_string(msg), "is")) {
  1056. renameBankSlot(rtosc_argument(msg,0).i, rtosc_argument(msg,1).s, master, osc);
  1057. } else if(strstr(msg, "swap-bank-slots") && !strcmp(rtosc_argument_string(msg), "ii")) {
  1058. swapBankSlot(rtosc_argument(msg,0).i, rtosc_argument(msg,1).i, master, osc);
  1059. } else if(strstr(msg, "clear-bank-slot") && !strcmp(rtosc_argument_string(msg), "i")) {
  1060. clearBankSlot(rtosc_argument(msg,0).i, master, osc);
  1061. } else if(strstr(msg, "/config/")) {
  1062. handleConfig(msg);
  1063. } else if(strstr(msg, "/presets/")) {
  1064. handlePresets(msg);
  1065. } else if(strstr(msg, "Padenabled") || strstr(msg, "Ppadenabled") || strstr(msg, "Psubenabled")) {
  1066. kitEnable(msg);
  1067. uToB->raw_write(msg);
  1068. } else if(sscanf(msg, "/part%d/clea%c", &npart, &testchr) == 2 && testchr == 'r') {
  1069. loadClearPart(npart);
  1070. } else if(!strcmp(msg, "/undo")) {
  1071. undo.seekHistory(-1);
  1072. } else if(!strcmp(msg, "/redo")) {
  1073. undo.seekHistory(+1);
  1074. } else
  1075. uToB->raw_write(msg);
  1076. }
  1077. void MiddleWareImpl::write(const char *path, const char *args, ...)
  1078. {
  1079. //We have a free buffer in the threadlink, so use it
  1080. va_list va;
  1081. va_start(va, args);
  1082. write(path, args, va);
  1083. va_end(va);
  1084. }
  1085. void MiddleWareImpl::write(const char *path, const char *args, va_list va)
  1086. {
  1087. //printf("is that a '%s' I see there?\n", path);
  1088. char *buffer = uToB->buffer();
  1089. unsigned len = uToB->buffer_size();
  1090. bool success = rtosc_vmessage(buffer, len, path, args, va);
  1091. //printf("working on '%s':'%s'\n",path, args);
  1092. if(success)
  1093. handleMsg(buffer);
  1094. else
  1095. warnx("Failed to write message to '%s'", path);
  1096. }
  1097. void MiddleWareImpl::warnMemoryLeaks(void)
  1098. {}
  1099. /******************************************************************************
  1100. * MidleWare Forwarding Stubs *
  1101. ******************************************************************************/
  1102. MiddleWare::MiddleWare(SYNTH_T synth, int prefered_port)
  1103. :impl(new MiddleWareImpl(this, synth, prefered_port))
  1104. {}
  1105. MiddleWare::~MiddleWare(void)
  1106. {
  1107. delete impl;
  1108. }
  1109. void MiddleWare::updateResources(Master *m)
  1110. {
  1111. impl->updateResources(m);
  1112. }
  1113. Master *MiddleWare::spawnMaster(void)
  1114. {
  1115. return impl->master;
  1116. }
  1117. Fl_Osc_Interface *MiddleWare::spawnUiApi(void)
  1118. {
  1119. return impl->osc;
  1120. }
  1121. void MiddleWare::tick(void)
  1122. {
  1123. impl->tick();
  1124. }
  1125. void MiddleWare::doReadOnlyOp(std::function<void()> fn)
  1126. {
  1127. impl->doReadOnlyOp(fn);
  1128. }
  1129. void MiddleWare::setUiCallback(void(*cb)(void*,const char *), void *ui)
  1130. {
  1131. impl->cb = cb;
  1132. impl->ui = ui;
  1133. }
  1134. void MiddleWare::setIdleCallback(void(*cb)(void*), void *ptr)
  1135. {
  1136. impl->idle = cb;
  1137. impl->idle_ptr = ptr;
  1138. }
  1139. void MiddleWare::transmitMsg(const char *msg)
  1140. {
  1141. impl->handleMsg(msg);
  1142. }
  1143. void MiddleWare::transmitMsg(const char *path, const char *args, ...)
  1144. {
  1145. char buffer[1024];
  1146. va_list va;
  1147. va_start(va,args);
  1148. if(rtosc_vmessage(buffer,1024,path,args,va))
  1149. transmitMsg(buffer);
  1150. else
  1151. fprintf(stderr, "Error in transmitMsg(...)\n");
  1152. va_end(va);
  1153. }
  1154. void MiddleWare::transmitMsg(const char *path, const char *args, va_list va)
  1155. {
  1156. char buffer[1024];
  1157. if(rtosc_vmessage(buffer, 1024, path, args, va))
  1158. transmitMsg(buffer);
  1159. else
  1160. fprintf(stderr, "Error in transmitMsg(va)n");
  1161. }
  1162. void MiddleWare::pendingSetProgram(int part, int program)
  1163. {
  1164. impl->pending_load[part]++;
  1165. impl->bToU->write("/setprogram", "cc", part, program);
  1166. }
  1167. std::string MiddleWare::activeUrl(void)
  1168. {
  1169. return impl->last_url;
  1170. }
  1171. void MiddleWare::activeUrl(std::string u)
  1172. {
  1173. impl->last_url = u;
  1174. }
  1175. const SYNTH_T &MiddleWare::getSynth(void) const
  1176. {
  1177. return impl->synth;
  1178. }
  1179. const char* MiddleWare::getServerAddress(void) const
  1180. {
  1181. return lo_server_get_url(impl->server);
  1182. }