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.

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