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.

2167 lines
67KB

  1. /*
  2. ZynAddSubFX - a software synthesizer
  3. MiddleWare.cpp - Glue Logic And Home Of Non-RT Operations
  4. Copyright (C) 2016 Mark McCurry
  5. This program is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU General Public License
  7. as published by the Free Software Foundation; either version 2
  8. of the License, or (at your option) any later version.
  9. */
  10. #include "MiddleWare.h"
  11. #include <cstring>
  12. #include <cstdio>
  13. #include <cstdlib>
  14. #include <fstream>
  15. #include <iostream>
  16. #include <dirent.h>
  17. #include <sys/stat.h>
  18. #include <rtosc/undo-history.h>
  19. #include <rtosc/thread-link.h>
  20. #include <rtosc/ports.h>
  21. #include <lo/lo.h>
  22. #include <unistd.h>
  23. #include "../UI/Connection.h"
  24. #include "../UI/Fl_Osc_Interface.h"
  25. #include <map>
  26. #include "Util.h"
  27. #include "CallbackRepeater.h"
  28. #include "Master.h"
  29. #include "Part.h"
  30. #include "PresetExtractor.h"
  31. #include "../Containers/MultiPseudoStack.h"
  32. #include "../Params/PresetsStore.h"
  33. #include "../Params/ADnoteParameters.h"
  34. #include "../Params/SUBnoteParameters.h"
  35. #include "../Params/PADnoteParameters.h"
  36. #include "../DSP/FFTwrapper.h"
  37. #include "../Synth/OscilGen.h"
  38. #include "../Nio/Nio.h"
  39. #include <string>
  40. #include <future>
  41. #include <atomic>
  42. #include <list>
  43. #define errx(...) {}
  44. #define warnx(...) {}
  45. #ifndef errx
  46. #include <err.h>
  47. #endif
  48. using std::string;
  49. int Pexitprogram = 0;
  50. /******************************************************************************
  51. * LIBLO And Reflection Code *
  52. * *
  53. * All messages that are handled are handled in a serial fashion. *
  54. * Thus, changes in the current interface sending messages can be encoded *
  55. * into the stream via events which simply echo back the active interface *
  56. ******************************************************************************/
  57. static void liblo_error_cb(int i, const char *m, const char *loc)
  58. {
  59. fprintf(stderr, "liblo :-( %d-%s@%s\n",i,m,loc);
  60. }
  61. void path_search(const char *m, const char *url)
  62. {
  63. using rtosc::Ports;
  64. using rtosc::Port;
  65. //assumed upper bound of 32 ports (may need to be resized)
  66. char types[256+1];
  67. rtosc_arg_t args[256];
  68. size_t pos = 0;
  69. const Ports *ports = NULL;
  70. const char *str = rtosc_argument(m,0).s;
  71. const char *needle = rtosc_argument(m,1).s;
  72. //zero out data
  73. memset(types, 0, sizeof(types));
  74. memset(args, 0, sizeof(args));
  75. if(!*str) {
  76. ports = &Master::ports;
  77. } else {
  78. const Port *port = Master::ports.apropos(rtosc_argument(m,0).s);
  79. if(port)
  80. ports = port->ports;
  81. }
  82. if(ports) {
  83. //RTness not confirmed here
  84. for(const Port &p:*ports) {
  85. if(strstr(p.name, needle) != p.name || !p.name)
  86. continue;
  87. types[pos] = 's';
  88. args[pos++].s = p.name;
  89. types[pos] = 'b';
  90. if(p.metadata && *p.metadata) {
  91. args[pos].b.data = (unsigned char*) p.metadata;
  92. auto tmp = rtosc::Port::MetaContainer(p.metadata);
  93. args[pos++].b.len = tmp.length();
  94. } else {
  95. args[pos].b.data = (unsigned char*) NULL;
  96. args[pos++].b.len = 0;
  97. }
  98. }
  99. }
  100. //Reply to requester [wow, these messages are getting huge...]
  101. char buffer[1024*20];
  102. size_t length = rtosc_amessage(buffer, sizeof(buffer), "/paths", types, args);
  103. if(length) {
  104. lo_message msg = lo_message_deserialise((void*)buffer, length, NULL);
  105. lo_address addr = lo_address_new_from_url(url);
  106. if(addr)
  107. lo_send_message(addr, buffer, msg);
  108. lo_address_free(addr);
  109. lo_message_free(msg);
  110. }
  111. }
  112. static int handler_function(const char *path, const char *types, lo_arg **argv,
  113. int argc, lo_message msg, void *user_data)
  114. {
  115. (void) types;
  116. (void) argv;
  117. (void) argc;
  118. MiddleWare *mw = (MiddleWare*)user_data;
  119. lo_address addr = lo_message_get_source(msg);
  120. if(addr) {
  121. const char *tmp = lo_address_get_url(addr);
  122. if(tmp != mw->activeUrl()) {
  123. mw->transmitMsg("/echo", "ss", "OSC_URL", tmp);
  124. mw->activeUrl(tmp);
  125. }
  126. free((void*)tmp);
  127. }
  128. char buffer[2048];
  129. memset(buffer, 0, sizeof(buffer));
  130. size_t size = 2048;
  131. lo_message_serialise(msg, path, buffer, &size);
  132. if(!strcmp(buffer, "/path-search") && !strcmp("ss", rtosc_argument_string(buffer))) {
  133. path_search(buffer, mw->activeUrl().c_str());
  134. } else if(buffer[0]=='/' && strrchr(buffer, '/')[1]) {
  135. mw->transmitMsg(rtosc::Ports::collapsePath(buffer));
  136. }
  137. return 0;
  138. }
  139. typedef void(*cb_t)(void*,const char*);
  140. //utility method (should be moved to a better location)
  141. template <class T, class V>
  142. std::vector<T> keys(const std::map<T,V> &m)
  143. {
  144. std::vector<T> vec;
  145. for(auto &kv: m)
  146. vec.push_back(kv.first);
  147. return vec;
  148. }
  149. /*****************************************************************************
  150. * Memory Deallocation *
  151. *****************************************************************************/
  152. void deallocate(const char *str, void *v)
  153. {
  154. //printf("deallocating a '%s' at '%p'\n", str, v);
  155. if(!strcmp(str, "Part"))
  156. delete (Part*)v;
  157. else if(!strcmp(str, "Master"))
  158. delete (Master*)v;
  159. else if(!strcmp(str, "fft_t"))
  160. delete[] (fft_t*)v;
  161. else if(!strcmp(str, "KbmInfo"))
  162. delete (KbmInfo*)v;
  163. else if(!strcmp(str, "SclInfo"))
  164. delete (SclInfo*)v;
  165. else if(!strcmp(str, "Microtonal"))
  166. delete (Microtonal*)v;
  167. else
  168. fprintf(stderr, "Unknown type '%s', leaking pointer %p!!\n", str, v);
  169. }
  170. /*****************************************************************************
  171. * PadSynth Setup *
  172. *****************************************************************************/
  173. void preparePadSynth(string path, PADnoteParameters *p, rtosc::RtData &d)
  174. {
  175. //printf("preparing padsynth parameters\n");
  176. assert(!path.empty());
  177. path += "sample";
  178. unsigned max = 0;
  179. p->sampleGenerator([&max,&path,&d]
  180. (unsigned N, PADnoteParameters::Sample &s)
  181. {
  182. max = max<N ? N : max;
  183. //printf("sending info to '%s'\n", (path+to_s(N)).c_str());
  184. d.chain((path+to_s(N)).c_str(), "ifb",
  185. s.size, s.basefreq, sizeof(float*), &s.smp);
  186. }, []{return false;});
  187. //clear out unused samples
  188. for(unsigned i = max+1; i < PAD_MAX_SAMPLES; ++i) {
  189. d.chain((path+to_s(i)).c_str(), "ifb",
  190. 0, 440.0f, sizeof(float*), NULL);
  191. }
  192. }
  193. /******************************************************************************
  194. * MIDI Serialization *
  195. * *
  196. ******************************************************************************/
  197. void saveMidiLearn(XMLwrapper &xml, const rtosc::MidiMappernRT &midi)
  198. {
  199. xml.beginbranch("midi-learn");
  200. for(auto value:midi.inv_map) {
  201. XmlNode binding("midi-binding");
  202. auto biject = std::get<3>(value.second);
  203. binding["osc-path"] = value.first;
  204. binding["coarse-CC"] = to_s(std::get<1>(value.second));
  205. binding["fine-CC"] = to_s(std::get<2>(value.second));
  206. binding["type"] = "i";
  207. binding["minimum"] = to_s(biject.min);
  208. binding["maximum"] = to_s(biject.max);
  209. xml.add(binding);
  210. }
  211. xml.endbranch();
  212. }
  213. void loadMidiLearn(XMLwrapper &xml, rtosc::MidiMappernRT &midi)
  214. {
  215. using rtosc::Port;
  216. if(xml.enterbranch("midi-learn")) {
  217. auto nodes = xml.getBranch();
  218. //TODO clear mapper
  219. for(auto node:nodes) {
  220. if(node.name != "midi-binding" ||
  221. !node.has("osc-path") ||
  222. !node.has("coarse-CC"))
  223. continue;
  224. const string path = node["osc-path"];
  225. const int CC = atoi(node["coarse-CC"].c_str());
  226. const Port *p = Master::ports.apropos(path.c_str());
  227. if(p) {
  228. printf("loading midi port...\n");
  229. midi.addNewMapper(CC, *p, path);
  230. } else {
  231. printf("unknown midi bindable <%s>\n", path.c_str());
  232. }
  233. }
  234. xml.exitbranch();
  235. } else
  236. printf("cannot find 'midi-learn' branch...\n");
  237. }
  238. /******************************************************************************
  239. * Non-RealTime Object Store *
  240. * *
  241. * *
  242. * Storage For Objects which need to be interfaced with outside the realtime *
  243. * thread (aka they have long lived operations which can be done out-of-band) *
  244. * *
  245. * - OscilGen instances as prepare() cannot be done in realtime and PAD *
  246. * depends on these instances *
  247. * - PADnoteParameter instances as applyparameters() cannot be done in *
  248. * realtime *
  249. * *
  250. * These instances are collected on every part change and kit change *
  251. ******************************************************************************/
  252. struct NonRtObjStore
  253. {
  254. std::map<std::string, void*> objmap;
  255. void extractMaster(Master *master)
  256. {
  257. for(int i=0; i < NUM_MIDI_PARTS; ++i) {
  258. extractPart(master->part[i], i);
  259. }
  260. }
  261. void extractPart(Part *part, int i)
  262. {
  263. for(int j=0; j < NUM_KIT_ITEMS; ++j) {
  264. auto &obj = part->kit[j];
  265. extractAD(obj.adpars, i, j);
  266. extractPAD(obj.padpars, i, j);
  267. }
  268. }
  269. void extractAD(ADnoteParameters *adpars, int i, int j)
  270. {
  271. std::string base = "/part"+to_s(i)+"/kit"+to_s(j)+"/";
  272. for(int k=0; k<NUM_VOICES; ++k) {
  273. std::string nbase = base+"adpars/VoicePar"+to_s(k)+"/";
  274. if(adpars) {
  275. auto &nobj = adpars->VoicePar[k];
  276. objmap[nbase+"OscilSmp/"] = nobj.OscilSmp;
  277. objmap[nbase+"FMSmp/"] = nobj.FMSmp;
  278. } else {
  279. objmap[nbase+"OscilSmp/"] = nullptr;
  280. objmap[nbase+"FMSmp/"] = nullptr;
  281. }
  282. }
  283. }
  284. void extractPAD(PADnoteParameters *padpars, int i, int j)
  285. {
  286. std::string base = "/part"+to_s(i)+"/kit"+to_s(j)+"/";
  287. for(int k=0; k<NUM_VOICES; ++k) {
  288. if(padpars) {
  289. objmap[base+"padpars/"] = padpars;
  290. objmap[base+"padpars/oscilgen/"] = padpars->oscilgen;
  291. } else {
  292. objmap[base+"padpars/"] = nullptr;
  293. objmap[base+"padpars/oscilgen/"] = nullptr;
  294. }
  295. }
  296. }
  297. void clear(void)
  298. {
  299. objmap.clear();
  300. }
  301. bool has(std::string loc)
  302. {
  303. return objmap.find(loc) != objmap.end();
  304. }
  305. void *get(std::string loc)
  306. {
  307. return objmap[loc];
  308. }
  309. void handleOscil(const char *msg, rtosc::RtData &d) {
  310. string obj_rl(d.message, msg);
  311. void *osc = get(obj_rl);
  312. assert(osc);
  313. strcpy(d.loc, obj_rl.c_str());
  314. d.obj = osc;
  315. OscilGen::non_realtime_ports.dispatch(msg, d);
  316. }
  317. void handlePad(const char *msg, rtosc::RtData &d) {
  318. string obj_rl(d.message, msg);
  319. void *pad = get(obj_rl);
  320. if(!strcmp(msg, "prepare")) {
  321. preparePadSynth(obj_rl, (PADnoteParameters*)pad, d);
  322. d.matches++;
  323. d.reply((obj_rl+"needPrepare").c_str(), "F");
  324. } else {
  325. if(!pad)
  326. return;
  327. strcpy(d.loc, obj_rl.c_str());
  328. d.obj = pad;
  329. PADnoteParameters::non_realtime_ports.dispatch(msg, d);
  330. if(rtosc_narguments(msg)) {
  331. if(!strcmp(msg, "oscilgen/prepare"))
  332. ; //ignore
  333. else {
  334. d.reply((obj_rl+"needPrepare").c_str(), "T");
  335. }
  336. }
  337. }
  338. }
  339. };
  340. /******************************************************************************
  341. * Realtime Parameter Store *
  342. * *
  343. * Storage for AD/PAD/SUB parameters which are allocated as needed by kits. *
  344. * Two classes of events affect this: *
  345. * 1. When a message to enable a kit is observed, then the kit is allocated *
  346. * and sent prior to the enable message. *
  347. * 2. When a part is allocated all part information is rebuilt *
  348. * *
  349. * (NOTE pointers aren't really needed here, just booleans on whether it has *
  350. * been allocated) *
  351. * This may be later utilized for copy/paste support *
  352. ******************************************************************************/
  353. struct ParamStore
  354. {
  355. ParamStore(void)
  356. {
  357. memset(add, 0, sizeof(add));
  358. memset(pad, 0, sizeof(pad));
  359. memset(sub, 0, sizeof(sub));
  360. }
  361. void extractPart(Part *part, int i)
  362. {
  363. for(int j=0; j < NUM_KIT_ITEMS; ++j) {
  364. auto kit = part->kit[j];
  365. add[i][j] = kit.adpars;
  366. sub[i][j] = kit.subpars;
  367. pad[i][j] = kit.padpars;
  368. }
  369. }
  370. ADnoteParameters *add[NUM_MIDI_PARTS][NUM_KIT_ITEMS];
  371. SUBnoteParameters *sub[NUM_MIDI_PARTS][NUM_KIT_ITEMS];
  372. PADnoteParameters *pad[NUM_MIDI_PARTS][NUM_KIT_ITEMS];
  373. };
  374. //XXX perhaps move this to Nio
  375. //(there needs to be some standard Nio stub file for this sort of stuff)
  376. namespace Nio
  377. {
  378. using std::get;
  379. rtosc::Ports ports = {
  380. {"sink-list:", 0, 0, [](const char *, rtosc::RtData &d) {
  381. auto list = Nio::getSinks();
  382. char *ret = rtosc_splat(d.loc, list);
  383. d.reply(ret);
  384. delete [] ret;
  385. }},
  386. {"source-list:", 0, 0, [](const char *, rtosc::RtData &d) {
  387. auto list = Nio::getSources();
  388. char *ret = rtosc_splat(d.loc, list);
  389. d.reply(ret);
  390. delete [] ret;
  391. }},
  392. {"source::s", 0, 0, [](const char *msg, rtosc::RtData &d) {
  393. if(rtosc_narguments(msg) == 0)
  394. d.reply(d.loc, "s", Nio::getSource().c_str());
  395. else
  396. Nio::setSource(rtosc_argument(msg,0).s);}},
  397. {"sink::s", 0, 0, [](const char *msg, rtosc::RtData &d) {
  398. if(rtosc_narguments(msg) == 0)
  399. d.reply(d.loc, "s", Nio::getSink().c_str());
  400. else
  401. Nio::setSink(rtosc_argument(msg,0).s);}},
  402. };
  403. }
  404. /* Implementation */
  405. class CarlaMiddleWareImpl
  406. {
  407. public:
  408. MiddleWare *parent;
  409. private:
  410. public:
  411. CarlaConfig* const config;
  412. CarlaMiddleWareImpl(MiddleWare *mw, SYNTH_T synth, CarlaConfig* config,
  413. int preferred_port);
  414. ~CarlaMiddleWareImpl(void);
  415. //Check offline vs online mode in plugins
  416. void heartBeat(Master *m);
  417. int64_t start_time_sec;
  418. int64_t start_time_nsec;
  419. bool offline;
  420. //Apply function while parameters are write locked
  421. void doReadOnlyOp(std::function<void()> read_only_fn);
  422. void doReadOnlyOpPlugin(std::function<void()> read_only_fn);
  423. bool doReadOnlyOpNormal(std::function<void()> read_only_fn, bool canfail=false);
  424. void savePart(int npart, const char *filename)
  425. {
  426. //Copy is needed as filename WILL get trashed during the rest of the run
  427. std::string fname = filename;
  428. //printf("saving part(%d,'%s')\n", npart, filename);
  429. doReadOnlyOp([this,fname,npart](){
  430. int res = master->part[npart]->saveXML(fname.c_str());
  431. (void)res;
  432. /*printf("results: '%s' '%d'\n",fname.c_str(), res);*/});
  433. }
  434. void loadPendingBank(int par, Bank &bank)
  435. {
  436. if(((unsigned int)par < bank.banks.size())
  437. && (bank.banks[par].dir != bank.bankfiletitle))
  438. bank.loadbank(bank.banks[par].dir);
  439. }
  440. void loadPart(int npart, const char *filename, Master *master)
  441. {
  442. actual_load[npart]++;
  443. if(actual_load[npart] != pending_load[npart])
  444. return;
  445. assert(actual_load[npart] <= pending_load[npart]);
  446. //load part in async fashion when possible
  447. #ifndef WIN32
  448. auto alloc = std::async(std::launch::async,
  449. [master,filename,this,npart](){
  450. Part *p = new Part(*master->memory, synth,
  451. master->time,
  452. config->cfg.GzipCompression,
  453. config->cfg.Interpolation,
  454. &master->microtonal, master->fft, &master->watcher,
  455. ("/part"+to_s(npart)+"/").c_str());
  456. if(p->loadXMLinstrument(filename))
  457. fprintf(stderr, "Warning: failed to load part<%s>!\n", filename);
  458. auto isLateLoad = [this,npart]{
  459. return actual_load[npart] != pending_load[npart];
  460. };
  461. p->applyparameters(isLateLoad);
  462. return p;});
  463. //Load the part
  464. if(idle) {
  465. while(alloc.wait_for(std::chrono::seconds(0)) != std::future_status::ready) {
  466. idle(idle_ptr);
  467. }
  468. }
  469. Part *p = alloc.get();
  470. #else
  471. Part *p = new Part(*master->memory, synth, master->time,
  472. config->cfg.GzipCompression,
  473. config->cfg.Interpolation,
  474. &master->microtonal, master->fft);
  475. if(p->loadXMLinstrument(filename))
  476. fprintf(stderr, "Warning: failed to load part<%s>!\n", filename);
  477. auto isLateLoad = [this,npart]{
  478. return actual_load[npart] != pending_load[npart];
  479. };
  480. p->applyparameters(isLateLoad);
  481. #endif
  482. obj_store.extractPart(p, npart);
  483. kits.extractPart(p, npart);
  484. //Give it to the backend and wait for the old part to return for
  485. //deallocation
  486. parent->transmitMsg("/load-part", "ib", npart, sizeof(Part*), &p);
  487. GUI::raiseUi(ui, "/damage", "s", ("/part"+to_s(npart)+"/").c_str());
  488. }
  489. //Load a new cleared Part instance
  490. void loadClearPart(int npart)
  491. {
  492. if(npart == -1)
  493. return;
  494. Part *p = new Part(*master->memory, synth,
  495. master->time,
  496. config->cfg.GzipCompression,
  497. config->cfg.Interpolation,
  498. &master->microtonal, master->fft);
  499. p->applyparameters();
  500. obj_store.extractPart(p, npart);
  501. kits.extractPart(p, npart);
  502. //Give it to the backend and wait for the old part to return for
  503. //deallocation
  504. parent->transmitMsg("/load-part", "ib", npart, sizeof(Part *), &p);
  505. GUI::raiseUi(ui, "/damage", "s", ("/part" + to_s(npart) + "/").c_str());
  506. }
  507. //Well, you don't get much crazier than changing out all of your RT
  508. //structures at once... TODO error handling
  509. void loadMaster(const char *filename)
  510. {
  511. Master *m = new Master(synth, config);
  512. m->uToB = uToB;
  513. m->bToU = bToU;
  514. if(filename) {
  515. if ( m->loadXML(filename) ) {
  516. delete m;
  517. return;
  518. }
  519. m->applyparameters();
  520. }
  521. //Update resource locator table
  522. updateResources(m);
  523. master = m;
  524. //Give it to the backend and wait for the old part to return for
  525. //deallocation
  526. parent->transmitMsg("/load-master", "b", sizeof(Master*), &m);
  527. }
  528. void loadXsz(const char *filename, rtosc::RtData &d)
  529. {
  530. Microtonal *micro = new Microtonal(master->gzip_compression);
  531. int err = micro->loadXML(filename);
  532. if(err) {
  533. d.reply("/alert", "s", "Error: Could not load the xsz file.");
  534. delete micro;
  535. } else
  536. d.chain("/microtonal/paste", "b", sizeof(void*), &micro);
  537. }
  538. void saveXsz(const char *filename, rtosc::RtData &d)
  539. {
  540. int err = 0;
  541. doReadOnlyOp([this,filename,&err](){
  542. err = master->microtonal.saveXML(filename);});
  543. if(err)
  544. d.reply("/alert", "s", "Error: Could not save the xsz file.");
  545. }
  546. void loadScl(const char *filename, rtosc::RtData &d)
  547. {
  548. SclInfo *scl = new SclInfo;
  549. int err=Microtonal::loadscl(*scl, filename);
  550. if(err) {
  551. d.reply("/alert", "s", "Error: Could not load the scl file.");
  552. delete scl;
  553. } else
  554. d.chain("/microtonal/paste_scl", "b", sizeof(void*), &scl);
  555. }
  556. void loadKbm(const char *filename, rtosc::RtData &d)
  557. {
  558. KbmInfo *kbm = new KbmInfo;
  559. int err=Microtonal::loadkbm(*kbm, filename);
  560. if(err) {
  561. d.reply("/alert", "s", "Error: Could not load the kbm file.");
  562. delete kbm;
  563. } else
  564. d.chain("/microtonal/paste_kbm", "b", sizeof(void*), &kbm);
  565. }
  566. void updateResources(Master *m)
  567. {
  568. obj_store.clear();
  569. obj_store.extractMaster(m);
  570. for(int i=0; i<NUM_MIDI_PARTS; ++i)
  571. kits.extractPart(m->part[i], i);
  572. }
  573. //If currently broadcasting messages
  574. bool broadcast = false;
  575. //If message should be forwarded through snoop ports
  576. bool forward = false;
  577. //if message is in order or out-of-order execution
  578. bool in_order = false;
  579. //If accepting undo events as user driven
  580. bool recording_undo = true;
  581. void bToUhandle(const char *rtmsg);
  582. void tick(void)
  583. {
  584. if(server)
  585. while(lo_server_recv_noblock(server, 0));
  586. while(bToU->hasNext()) {
  587. const char *rtmsg = bToU->read();
  588. bToUhandle(rtmsg);
  589. }
  590. while(auto *m = multi_thread_source.read()) {
  591. handleMsg(m->memory);
  592. multi_thread_source.free(m);
  593. }
  594. autoSave.tick();
  595. heartBeat(master);
  596. //XXX This might have problems with a master swap operation
  597. if(offline)
  598. master->runOSC(0,0,true);
  599. }
  600. void kitEnable(const char *msg);
  601. void kitEnable(int part, int kit, int type);
  602. // Handle an event with special cases
  603. void handleMsg(const char *msg);
  604. void write(const char *path, const char *args, ...);
  605. void write(const char *path, const char *args, va_list va);
  606. void currentUrl(string addr)
  607. {
  608. curr_url = addr;
  609. known_remotes.insert(addr);
  610. }
  611. // Send a message to a remote client
  612. void sendToRemote(const char *msg, std::string dest);
  613. // Send a message to the current remote client
  614. void sendToCurrentRemote(const char *msg)
  615. {
  616. sendToRemote(msg, in_order ? curr_url : last_url);
  617. }
  618. // Broadcast a message to all listening remote clients
  619. void broadcastToRemote(const char *msg);
  620. /*
  621. * Provides a mapping for non-RT objects stored inside the backend
  622. * - Oscilgen almost all parameters can be safely set
  623. * - Padnote can have anything set on its oscilgen and a very small set
  624. * of general parameters
  625. */
  626. NonRtObjStore obj_store;
  627. //This code will own the pointer to master, be prepared for odd things if
  628. //this assumption is broken
  629. Master *master;
  630. //The ONLY means that any chunk of UI code should have for interacting with the
  631. //backend
  632. Fl_Osc_Interface *osc;
  633. //Synth Engine Parameters
  634. ParamStore kits;
  635. //Callback When Waiting on async events
  636. void(*idle)(void*);
  637. void* idle_ptr;
  638. //General UI callback
  639. cb_t cb;
  640. //UI handle
  641. void *ui;
  642. std::atomic_int pending_load[NUM_MIDI_PARTS];
  643. std::atomic_int actual_load[NUM_MIDI_PARTS];
  644. //Undo/Redo
  645. rtosc::UndoHistory undo;
  646. //MIDI Learn
  647. rtosc::MidiMappernRT midi_mapper;
  648. //Link To the Realtime
  649. rtosc::ThreadLink *bToU;
  650. rtosc::ThreadLink *uToB;
  651. //Link to the unknown
  652. MultiQueue multi_thread_source;
  653. //LIBLO
  654. lo_server server;
  655. string last_url, curr_url;
  656. std::set<string> known_remotes;
  657. //Synthesis Rate Parameters
  658. const SYNTH_T synth;
  659. PresetsStore presetsstore;
  660. CallbackRepeater autoSave;
  661. };
  662. /*****************************************************************************
  663. * Data Object for Non-RT Class Dispatch *
  664. *****************************************************************************/
  665. class MwDataObj:public rtosc::RtData
  666. {
  667. public:
  668. MwDataObj(CarlaMiddleWareImpl *mwi_)
  669. {
  670. loc_size = 1024;
  671. loc = new char[loc_size];
  672. memset(loc, 0, loc_size);
  673. buffer = new char[4*4096];
  674. memset(buffer, 0, 4*4096);
  675. obj = mwi_;
  676. mwi = mwi_;
  677. forwarded = false;
  678. }
  679. ~MwDataObj(void)
  680. {
  681. delete[] loc;
  682. delete[] buffer;
  683. }
  684. //Replies and broadcasts go to the remote
  685. //Chain calls repeat the call into handle()
  686. //Forward calls send the message directly to the realtime
  687. virtual void reply(const char *path, const char *args, ...)
  688. {
  689. //printf("reply building '%s'\n", path);
  690. va_list va;
  691. va_start(va,args);
  692. if(!strcmp(path, "/forward")) { //forward the information to the backend
  693. args++;
  694. path = va_arg(va, const char *);
  695. rtosc_vmessage(buffer,4*4096,path,args,va);
  696. } else {
  697. rtosc_vmessage(buffer,4*4096,path,args,va);
  698. reply(buffer);
  699. }
  700. va_end(va);
  701. }
  702. virtual void replyArray(const char *path, const char *args, rtosc_arg_t *argd) override
  703. {
  704. //printf("reply building '%s'\n", path);
  705. if(!strcmp(path, "/forward")) { //forward the information to the backend
  706. args++;
  707. rtosc_amessage(buffer,4*4096,path,args,argd);
  708. } else {
  709. rtosc_amessage(buffer,4*4096,path,args,argd);
  710. reply(buffer);
  711. }
  712. }
  713. virtual void reply(const char *msg){
  714. mwi->sendToCurrentRemote(msg);
  715. };
  716. //virtual void broadcast(const char *path, const char *args, ...){(void)path;(void)args;};
  717. //virtual void broadcast(const char *msg){(void)msg;};
  718. virtual void chain(const char *msg) override
  719. {
  720. assert(msg);
  721. // printf("chain call on <%s>\n", msg);
  722. mwi->handleMsg(msg);
  723. }
  724. virtual void chain(const char *path, const char *args, ...) override
  725. {
  726. assert(path);
  727. va_list va;
  728. va_start(va,args);
  729. rtosc_vmessage(buffer,4*4096,path,args,va);
  730. chain(buffer);
  731. va_end(va);
  732. }
  733. virtual void forward(const char *) override
  734. {
  735. forwarded = true;
  736. }
  737. bool forwarded;
  738. private:
  739. char *buffer;
  740. CarlaMiddleWareImpl *mwi;
  741. };
  742. static std::vector<std::string> getFiles(const char *folder, bool finddir)
  743. {
  744. DIR *dir = opendir(folder);
  745. if(dir == NULL) {
  746. return {};
  747. }
  748. struct dirent *fn;
  749. std::vector<string> files;
  750. while((fn = readdir(dir))) {
  751. #ifndef WIN32
  752. bool is_dir = fn->d_type & DT_DIR;
  753. //it could still be a symbolic link
  754. if(!is_dir) {
  755. string path = string(folder) + "/" + fn->d_name;
  756. struct stat buf;
  757. memset((void*)&buf, 0, sizeof(buf));
  758. int err = stat(path.c_str(), &buf);
  759. if(err)
  760. printf("[Zyn:Error] stat cannot handle <%s>:%d\n", path.c_str(), err);
  761. if(S_ISDIR(buf.st_mode)) {
  762. is_dir = true;
  763. }
  764. }
  765. #else
  766. std::string darn_windows = folder + std::string("/") + std::string(fn->d_name);
  767. printf("attr on <%s> => %x\n", darn_windows.c_str(), GetFileAttributes(darn_windows.c_str()));
  768. printf("error = %x\n", INVALID_FILE_ATTRIBUTES);
  769. bool is_dir = GetFileAttributes(darn_windows.c_str()) & FILE_ATTRIBUTE_DIRECTORY;
  770. #endif
  771. if(finddir == is_dir)
  772. files.push_back(fn->d_name);
  773. }
  774. closedir(dir);
  775. std::sort(begin(files), end(files));
  776. return files;
  777. }
  778. static int extractInt(const char *msg)
  779. {
  780. const char *mm = msg;
  781. while(*mm && !isdigit(*mm)) ++mm;
  782. if(isdigit(*mm))
  783. return atoi(mm);
  784. return -1;
  785. }
  786. static const char *chomp(const char *msg)
  787. {
  788. while(*msg && *msg!='/') ++msg; \
  789. msg = *msg ? msg+1 : msg;
  790. return msg;
  791. };
  792. using rtosc::RtData;
  793. #define rObject Bank
  794. #define rBegin [](const char *msg, RtData &d) { (void)msg;(void)d;\
  795. rObject &impl = *((rObject*)d.obj);(void)impl;
  796. #define rEnd }
  797. /*****************************************************************************
  798. * Instrument Banks *
  799. * *
  800. * Banks and presets in general are not classed as realtime safe *
  801. * *
  802. * The supported operations are: *
  803. * - Load Names *
  804. * - Load Bank *
  805. * - Refresh List of Banks *
  806. *****************************************************************************/
  807. extern const rtosc::Ports bankPorts;
  808. const rtosc::Ports bankPorts = {
  809. {"rescan:", 0, 0,
  810. rBegin;
  811. impl.rescanforbanks();
  812. //Send updated banks
  813. int i = 0;
  814. for(auto &elm : impl.banks)
  815. d.reply("/bank/bank_select", "iss", i++, elm.name.c_str(), elm.dir.c_str());
  816. d.reply("/bank/bank_select", "i", impl.bankpos);
  817. rEnd},
  818. {"bank_list:", 0, 0,
  819. rBegin;
  820. #define MAX_BANKS 256
  821. char types[MAX_BANKS*2+1]={0};
  822. rtosc_arg_t args[MAX_BANKS*2];
  823. int i = 0;
  824. for(auto &elm : impl.banks) {
  825. types[i] = types [i + 1] = 's';
  826. args[i++].s = elm.name.c_str();
  827. args[i++].s = elm.dir.c_str();
  828. }
  829. d.replyArray("/bank/bank_list", types, args);
  830. #undef MAX_BANKS
  831. rEnd},
  832. {"types:", 0, 0,
  833. rBegin;
  834. const char *types[17];
  835. types[ 0] = "None";
  836. types[ 1] = "Piano";
  837. types[ 2] = "Chromatic Percussion";
  838. types[ 3] = "Organ";
  839. types[ 4] = "Guitar";
  840. types[ 5] = "Bass";
  841. types[ 6] = "Solo Strings";
  842. types[ 7] = "Ensemble";
  843. types[ 8] = "Brass";
  844. types[ 9] = "Reed";
  845. types[10] = "Pipe";
  846. types[11] = "Synth Lead";
  847. types[12] = "Synth Pad";
  848. types[13] = "Synth Effects";
  849. types[14] = "Ethnic";
  850. types[15] = "Percussive";
  851. types[16] = "Sound Effects";
  852. char t[17+1]={0};
  853. rtosc_arg_t args[17];
  854. for(int i=0; i<17; ++i) {
  855. t[i] = 's';
  856. args[i].s = types[i];
  857. }
  858. d.replyArray("/bank/types", t, args);
  859. rEnd},
  860. {"tags:", 0, 0,
  861. rBegin;
  862. const char *types[8];
  863. types[ 0] = "fast";
  864. types[ 1] = "slow";
  865. types[ 2] = "saw";
  866. types[ 3] = "bell";
  867. types[ 4] = "lead";
  868. types[ 5] = "ambient";
  869. types[ 6] = "horn";
  870. types[ 7] = "alarm";
  871. char t[8+1]={0};
  872. rtosc_arg_t args[8];
  873. for(int i=0; i<8; ++i) {
  874. t[i] = 's';
  875. args[i].s = types[i];
  876. }
  877. d.replyArray(d.loc, t, args);
  878. rEnd},
  879. {"slot#1024:", 0, 0,
  880. rBegin;
  881. const int loc = extractInt(msg);
  882. if(loc >= BANK_SIZE)
  883. return;
  884. d.reply("/bankview", "iss",
  885. loc, impl.ins[loc].name.c_str(),
  886. impl.ins[loc].filename.c_str());
  887. rEnd},
  888. {"banks:", 0, 0,
  889. rBegin;
  890. int i = 0;
  891. for(auto &elm : impl.banks)
  892. d.reply("/bank/bank_select", "iss", i++, elm.name.c_str(), elm.dir.c_str());
  893. rEnd},
  894. {"bank_select::i", 0, 0,
  895. rBegin
  896. if(rtosc_narguments(msg)) {
  897. const int pos = rtosc_argument(msg, 0).i;
  898. d.reply(d.loc, "i", pos);
  899. if(impl.bankpos != pos) {
  900. impl.bankpos = pos;
  901. impl.loadbank(impl.banks[pos].dir);
  902. //Reload bank slots
  903. for(int i=0; i<BANK_SIZE; ++i)
  904. d.reply("/bankview", "iss",
  905. i, impl.ins[i].name.c_str(),
  906. impl.ins[i].filename.c_str());
  907. }
  908. } else
  909. d.reply("/bank/bank_select", "i", impl.bankpos);
  910. rEnd},
  911. {"rename_slot:is", 0, 0,
  912. rBegin;
  913. const int slot = rtosc_argument(msg, 0).i;
  914. const char *name = rtosc_argument(msg, 1).s;
  915. const int err = impl.setname(slot, name, -1);
  916. if(err) {
  917. d.reply("/alert", "s",
  918. "Failed To Rename Bank Slot, please check file permissions");
  919. }
  920. rEnd},
  921. {"swap_slots:ii", 0, 0,
  922. rBegin;
  923. const int slota = rtosc_argument(msg, 0).i;
  924. const int slotb = rtosc_argument(msg, 1).i;
  925. const int err = impl.swapslot(slota, slotb);
  926. if(err)
  927. d.reply("/alert", "s",
  928. "Failed To Swap Bank Slots, please check file permissions");
  929. rEnd},
  930. {"clear_slot:i", 0, 0,
  931. rBegin;
  932. const int slot = rtosc_argument(msg, 0).i;
  933. const int err = impl.clearslot(slot);
  934. if(err)
  935. d.reply("/alert", "s",
  936. "Failed To Clear Bank Slot, please check file permissions");
  937. rEnd},
  938. {"msb::i", 0, 0,
  939. rBegin;
  940. if(rtosc_narguments(msg))
  941. impl.setMsb(rtosc_argument(msg, 0).i);
  942. else
  943. d.reply(d.loc, "i", impl.bank_msb);
  944. rEnd},
  945. {"lsb::i", 0, 0,
  946. rBegin;
  947. if(rtosc_narguments(msg))
  948. impl.setLsb(rtosc_argument(msg, 0).i);
  949. else
  950. d.reply(d.loc, "i", impl.bank_lsb);
  951. rEnd},
  952. {"newbank:s", 0, 0,
  953. rBegin;
  954. int err = impl.newbank(rtosc_argument(msg, 0).s);
  955. if(err)
  956. d.reply("/alert", "s", "Error: Could not make a new bank (directory)..");
  957. rEnd},
  958. {"search:s", 0, 0,
  959. rBegin;
  960. auto res = impl.search(rtosc_argument(msg, 0).s);
  961. #define MAX_SEARCH 300
  962. char res_type[MAX_SEARCH+1] = {0};
  963. rtosc_arg_t res_dat[MAX_SEARCH] = {0};
  964. for(unsigned i=0; i<res.size() && i<MAX_SEARCH; ++i) {
  965. res_type[i] = 's';
  966. res_dat[i].s = res[i].c_str();
  967. }
  968. d.replyArray("/bank/search_results", res_type, res_dat);
  969. #undef MAX_SEARCH
  970. rEnd},
  971. {"blist:s", 0, 0,
  972. rBegin;
  973. auto res = impl.blist(rtosc_argument(msg, 0).s);
  974. #define MAX_SEARCH 300
  975. char res_type[MAX_SEARCH+1] = {0};
  976. rtosc_arg_t res_dat[MAX_SEARCH] = {0};
  977. for(unsigned i=0; i<res.size() && i<MAX_SEARCH; ++i) {
  978. res_type[i] = 's';
  979. res_dat[i].s = res[i].c_str();
  980. }
  981. d.replyArray("/bank/search_results", res_type, res_dat);
  982. #undef MAX_SEARCH
  983. rEnd},
  984. {"search_results:", 0, 0,
  985. rBegin;
  986. d.reply("/bank/search_results", "");
  987. rEnd},
  988. };
  989. /******************************************************************************
  990. * MiddleWare Snooping Ports *
  991. * *
  992. * These ports handle: *
  993. * - Events going to the realtime thread which cannot be safely handled *
  994. * there *
  995. * - Events generated by the realtime thread which are not destined for a *
  996. * user interface *
  997. ******************************************************************************/
  998. #undef rObject
  999. #define rObject CarlaMiddleWareImpl
  1000. #ifndef STRINGIFY
  1001. #define STRINGIFY2(a) #a
  1002. #define STRINGIFY(a) STRINGIFY2(a)
  1003. #endif
  1004. /*
  1005. * BASE/part#/kititem#
  1006. * BASE/part#/kit#/adpars/voice#/oscil/\*
  1007. * BASE/part#/kit#/adpars/voice#/mod-oscil/\*
  1008. * BASE/part#/kit#/padpars/prepare
  1009. * BASE/part#/kit#/padpars/oscil/\*
  1010. */
  1011. static rtosc::Ports middwareSnoopPorts = {
  1012. {"part#" STRINGIFY(NUM_MIDI_PARTS)
  1013. "/kit#" STRINGIFY(NUM_KIT_ITEMS) "/adpars/VoicePar#"
  1014. STRINGIFY(NUM_VOICES) "/OscilSmp/", 0, &OscilGen::non_realtime_ports,
  1015. rBegin;
  1016. impl.obj_store.handleOscil(chomp(chomp(chomp(chomp(chomp(msg))))), d);
  1017. rEnd},
  1018. {"part#" STRINGIFY(NUM_MIDI_PARTS)
  1019. "/kit#" STRINGIFY(NUM_KIT_ITEMS)
  1020. "/adpars/VoicePar#" STRINGIFY(NUM_VOICES) "/FMSmp/", 0, &OscilGen::non_realtime_ports,
  1021. rBegin
  1022. impl.obj_store.handleOscil(chomp(chomp(chomp(chomp(chomp(msg))))), d);
  1023. rEnd},
  1024. {"part#" STRINGIFY(NUM_MIDI_PARTS)
  1025. "/kit#" STRINGIFY(NUM_KIT_ITEMS) "/padpars/", 0, &PADnoteParameters::non_realtime_ports,
  1026. rBegin
  1027. impl.obj_store.handlePad(chomp(chomp(chomp(msg))), d);
  1028. rEnd},
  1029. {"bank/", 0, &bankPorts,
  1030. rBegin;
  1031. d.obj = &impl.master->bank;
  1032. bankPorts.dispatch(chomp(msg),d);
  1033. rEnd},
  1034. {"bank/save_to_slot:ii", 0, 0,
  1035. rBegin;
  1036. const int part_id = rtosc_argument(msg, 0).i;
  1037. const int slot = rtosc_argument(msg, 1).i;
  1038. int err = 0;
  1039. impl.doReadOnlyOp([&impl,slot,part_id,&err](){
  1040. err = impl.master->bank.savetoslot(slot, impl.master->part[part_id]);});
  1041. if(err) {
  1042. char buffer[1024];
  1043. rtosc_message(buffer, 1024, "/alert", "s",
  1044. "Failed To Save To Bank Slot, please check file permissions");
  1045. GUI::raiseUi(impl.ui, buffer);
  1046. }
  1047. rEnd},
  1048. {"config/", 0, &CarlaConfig::ports,
  1049. rBegin;
  1050. d.obj = impl.config;
  1051. CarlaConfig::ports.dispatch(chomp(msg), d);
  1052. rEnd},
  1053. {"presets/", 0, &real_preset_ports, [](const char *msg, RtData &d) {
  1054. CarlaMiddleWareImpl *obj = (CarlaMiddleWareImpl*)d.obj;
  1055. d.obj = (void*)obj->parent;
  1056. real_preset_ports.dispatch(chomp(msg), d);
  1057. if(strstr(msg, "paste") && rtosc_argument_string(msg)[0] == 's')
  1058. d.reply("/damage", "s", rtosc_argument(msg, 0).s);
  1059. }},
  1060. {"io/", 0, &Nio::ports, [](const char *msg, RtData &d) {
  1061. Nio::ports.dispatch(chomp(msg), d);}},
  1062. {"part*/kit*/{Padenabled,Ppadenabled,Psubenabled}:T:F", 0, 0,
  1063. rBegin;
  1064. impl.kitEnable(msg);
  1065. d.forward();
  1066. rEnd},
  1067. {"save_xlz:s", 0, 0,
  1068. rBegin;
  1069. const char *file = rtosc_argument(msg, 0).s;
  1070. XMLwrapper xml;
  1071. saveMidiLearn(xml, impl.midi_mapper);
  1072. xml.saveXMLfile(file, impl.master->gzip_compression);
  1073. rEnd},
  1074. {"load_xlz:s", 0, 0,
  1075. rBegin;
  1076. const char *file = rtosc_argument(msg, 0).s;
  1077. XMLwrapper xml;
  1078. xml.loadXMLfile(file);
  1079. loadMidiLearn(xml, impl.midi_mapper);
  1080. rEnd},
  1081. {"clear_xlz:", 0, 0,
  1082. rBegin;
  1083. impl.midi_mapper.clear();
  1084. rEnd},
  1085. //scale file stuff
  1086. {"load_xsz:s", 0, 0,
  1087. rBegin;
  1088. const char *file = rtosc_argument(msg, 0).s;
  1089. impl.loadXsz(file, d);
  1090. rEnd},
  1091. {"save_xsz:s", 0, 0,
  1092. rBegin;
  1093. const char *file = rtosc_argument(msg, 0).s;
  1094. impl.saveXsz(file, d);
  1095. rEnd},
  1096. {"load_scl:s", 0, 0,
  1097. rBegin;
  1098. const char *file = rtosc_argument(msg, 0).s;
  1099. impl.loadScl(file, d);
  1100. rEnd},
  1101. {"load_kbm:s", 0, 0,
  1102. rBegin;
  1103. const char *file = rtosc_argument(msg, 0).s;
  1104. impl.loadKbm(file, d);
  1105. rEnd},
  1106. {"save_xmz:s", 0, 0,
  1107. rBegin;
  1108. const char *file = rtosc_argument(msg, 0).s;
  1109. //Copy is needed as filename WILL get trashed during the rest of the run
  1110. impl.doReadOnlyOp([&impl,file](){
  1111. int res = impl.master->saveXML(file);
  1112. (void)res;});
  1113. rEnd},
  1114. {"save_xiz:is", 0, 0,
  1115. rBegin;
  1116. const int part_id = rtosc_argument(msg,0).i;
  1117. const char *file = rtosc_argument(msg,1).s;
  1118. impl.savePart(part_id, file);
  1119. rEnd},
  1120. {"file_home_dir:", 0, 0,
  1121. rBegin;
  1122. const char *home = getenv("PWD");
  1123. if(!home)
  1124. home = getenv("HOME");
  1125. if(!home)
  1126. home = getenv("USERPROFILE");
  1127. if(!home)
  1128. home = getenv("HOMEPATH");
  1129. if(!home)
  1130. home = "/";
  1131. string home_ = home;
  1132. #ifndef WIN32
  1133. if(home_[home_.length()-1] != '/')
  1134. home_ += '/';
  1135. #endif
  1136. d.reply(d.loc, "s", home_.c_str());
  1137. rEnd},
  1138. {"file_list_files:s", 0, 0,
  1139. rBegin;
  1140. const char *folder = rtosc_argument(msg, 0).s;
  1141. auto files = getFiles(folder, false);
  1142. const int N = files.size();
  1143. rtosc_arg_t *args = new rtosc_arg_t[N];
  1144. char *types = new char[N+1];
  1145. types[N] = 0;
  1146. for(int i=0; i<N; ++i) {
  1147. args[i].s = files[i].c_str();
  1148. types[i] = 's';
  1149. }
  1150. d.replyArray(d.loc, types, args);
  1151. delete [] types;
  1152. delete [] args;
  1153. rEnd},
  1154. {"file_list_dirs:s", 0, 0,
  1155. rBegin;
  1156. const char *folder = rtosc_argument(msg, 0).s;
  1157. auto files = getFiles(folder, true);
  1158. const int N = files.size();
  1159. rtosc_arg_t *args = new rtosc_arg_t[N];
  1160. char *types = new char[N+1];
  1161. types[N] = 0;
  1162. for(int i=0; i<N; ++i) {
  1163. args[i].s = files[i].c_str();
  1164. types[i] = 's';
  1165. }
  1166. d.replyArray(d.loc, types, args);
  1167. delete [] types;
  1168. delete [] args;
  1169. rEnd},
  1170. {"reload_auto_save:i", 0, 0,
  1171. rBegin
  1172. const int save_id = rtosc_argument(msg,0).i;
  1173. const string save_dir = string(getenv("HOME")) + "/.local";
  1174. const string save_file = "zynaddsubfx-"+to_s(save_id)+"-autosave.xmz";
  1175. const string save_loc = save_dir + "/" + save_file;
  1176. impl.loadMaster(save_loc.c_str());
  1177. //XXX it would be better to remove the autosave after there is a new
  1178. //autosave, but this method should work for non-immediate crashes :-|
  1179. remove(save_loc.c_str());
  1180. rEnd},
  1181. {"delete_auto_save:i", 0, 0,
  1182. rBegin
  1183. const int save_id = rtosc_argument(msg,0).i;
  1184. const string save_dir = string(getenv("HOME")) + "/.local";
  1185. const string save_file = "zynaddsubfx-"+to_s(save_id)+"-autosave.xmz";
  1186. const string save_loc = save_dir + "/" + save_file;
  1187. remove(save_loc.c_str());
  1188. rEnd},
  1189. {"load_xmz:s", 0, 0,
  1190. rBegin;
  1191. const char *file = rtosc_argument(msg, 0).s;
  1192. impl.loadMaster(file);
  1193. d.reply("/damage", "s", "/");
  1194. rEnd},
  1195. {"reset_master:", 0, 0,
  1196. rBegin;
  1197. impl.loadMaster(NULL);
  1198. d.reply("/damage", "s", "/");
  1199. rEnd},
  1200. {"load_xiz:is", 0, 0,
  1201. rBegin;
  1202. const int part_id = rtosc_argument(msg,0).i;
  1203. const char *file = rtosc_argument(msg,1).s;
  1204. impl.pending_load[part_id]++;
  1205. impl.loadPart(part_id, file, impl.master);
  1206. rEnd},
  1207. {"load-part:is", 0, 0,
  1208. rBegin;
  1209. const int part_id = rtosc_argument(msg,0).i;
  1210. const char *file = rtosc_argument(msg,1).s;
  1211. impl.pending_load[part_id]++;
  1212. impl.loadPart(part_id, file, impl.master);
  1213. rEnd},
  1214. {"load-part:iss", 0, 0,
  1215. rBegin;
  1216. const int part_id = rtosc_argument(msg,0).i;
  1217. const char *file = rtosc_argument(msg,1).s;
  1218. const char *name = rtosc_argument(msg,2).s;
  1219. impl.pending_load[part_id]++;
  1220. impl.loadPart(part_id, file, impl.master);
  1221. impl.uToB->write(("/part"+to_s(part_id)+"/Pname").c_str(), "s",
  1222. name);
  1223. rEnd},
  1224. {"setprogram:i:c", 0, 0,
  1225. rBegin;
  1226. Bank &bank = impl.master->bank;
  1227. const int slot = rtosc_argument(msg, 0).i + 128*bank.bank_lsb;
  1228. if(slot < BANK_SIZE) {
  1229. impl.pending_load[0]++;
  1230. impl.loadPart(0, impl.master->bank.ins[slot].filename.c_str(), impl.master);
  1231. impl.uToB->write("/part0/Pname", "s", impl.master->bank.ins[slot].name.c_str());
  1232. }
  1233. rEnd},
  1234. {"part#16/clear:", 0, 0,
  1235. rBegin;
  1236. int id = extractInt(msg);
  1237. impl.loadClearPart(id);
  1238. d.reply("/damage", "s", ("/part"+to_s(id)).c_str());
  1239. rEnd},
  1240. {"undo:", 0, 0,
  1241. rBegin;
  1242. impl.undo.seekHistory(-1);
  1243. rEnd},
  1244. {"redo:", 0, 0,
  1245. rBegin;
  1246. impl.undo.seekHistory(+1);
  1247. rEnd},
  1248. //port to observe the midi mappings
  1249. {"midi-learn-values:", 0, 0,
  1250. rBegin;
  1251. auto &midi = impl.midi_mapper;
  1252. auto key = keys(midi.inv_map);
  1253. //cc-id, path, min, max
  1254. #define MAX_MIDI 32
  1255. rtosc_arg_t args[MAX_MIDI*4];
  1256. char argt[MAX_MIDI*4+1] = {0};
  1257. int j=0;
  1258. for(unsigned i=0; i<key.size() && i<MAX_MIDI; ++i) {
  1259. auto val = midi.inv_map[key[i]];
  1260. if(std::get<1>(val) == -1)
  1261. continue;
  1262. argt[4*j+0] = 'i';
  1263. args[4*j+0].i = std::get<1>(val);
  1264. argt[4*j+1] = 's';
  1265. args[4*j+1].s = key[i].c_str();
  1266. argt[4*j+2] = 'i';
  1267. args[4*j+2].i = 0;
  1268. argt[4*j+3] = 'i';
  1269. args[4*j+3].i = 127;
  1270. j++;
  1271. }
  1272. d.replyArray(d.loc, argt, args);
  1273. #undef MAX_MIDI
  1274. rEnd},
  1275. {"learn:s", 0, 0,
  1276. rBegin;
  1277. string addr = rtosc_argument(msg, 0).s;
  1278. auto &midi = impl.midi_mapper;
  1279. auto map = midi.getMidiMappingStrings();
  1280. if(map.find(addr) != map.end())
  1281. midi.map(addr.c_str(), false);
  1282. else
  1283. midi.map(addr.c_str(), true);
  1284. rEnd},
  1285. {"unlearn:s", 0, 0,
  1286. rBegin;
  1287. string addr = rtosc_argument(msg, 0).s;
  1288. auto &midi = impl.midi_mapper;
  1289. auto map = midi.getMidiMappingStrings();
  1290. midi.unMap(addr.c_str(), false);
  1291. midi.unMap(addr.c_str(), true);
  1292. rEnd},
  1293. //drop this message into the abyss
  1294. {"ui/title:", 0, 0, [](const char *msg, RtData &d) {}},
  1295. {"quit:", 0, 0, [](const char *, RtData&) {Pexitprogram = 1;}},
  1296. };
  1297. static rtosc::Ports middlewareReplyPorts = {
  1298. {"echo:ss", 0, 0,
  1299. rBegin;
  1300. const char *type = rtosc_argument(msg, 0).s;
  1301. const char *url = rtosc_argument(msg, 1).s;
  1302. if(!strcmp(type, "OSC_URL"))
  1303. impl.currentUrl(url);
  1304. rEnd},
  1305. {"free:sb", 0, 0,
  1306. rBegin;
  1307. const char *type = rtosc_argument(msg, 0).s;
  1308. void *ptr = *(void**)rtosc_argument(msg, 1).b.data;
  1309. deallocate(type, ptr);
  1310. rEnd},
  1311. {"request-memory:", 0, 0,
  1312. rBegin;
  1313. //Generate out more memory for the RT memory pool
  1314. //5MBi chunk
  1315. size_t N = 5*1024*1024;
  1316. void *mem = malloc(N);
  1317. impl.uToB->write("/add-rt-memory", "bi", sizeof(void*), &mem, N);
  1318. rEnd},
  1319. {"setprogram:cc:ii", 0, 0,
  1320. rBegin;
  1321. Bank &bank = impl.master->bank;
  1322. const int part = rtosc_argument(msg, 0).i;
  1323. const int program = rtosc_argument(msg, 1).i + 128*bank.bank_lsb;
  1324. impl.loadPart(part, impl.master->bank.ins[program].filename.c_str(), impl.master);
  1325. impl.uToB->write(("/part"+to_s(part)+"/Pname").c_str(), "s", impl.master->bank.ins[program].name.c_str());
  1326. rEnd},
  1327. {"setbank:c", 0, 0,
  1328. rBegin;
  1329. impl.loadPendingBank(rtosc_argument(msg,0).i, impl.master->bank);
  1330. rEnd},
  1331. {"undo_pause:", 0, 0, rBegin; impl.recording_undo = false; rEnd},
  1332. {"undo_resume:", 0, 0, rBegin; impl.recording_undo = true; rEnd},
  1333. {"undo_change", 0, 0,
  1334. rBegin;
  1335. if(impl.recording_undo)
  1336. impl.undo.recordEvent(msg);
  1337. rEnd},
  1338. {"midi-use-CC:i", 0, 0,
  1339. rBegin;
  1340. impl.midi_mapper.useFreeID(rtosc_argument(msg, 0).i);
  1341. rEnd},
  1342. {"broadcast:", 0, 0, rBegin; impl.broadcast = true; rEnd},
  1343. {"forward:", 0, 0, rBegin; impl.forward = true; rEnd},
  1344. };
  1345. #undef rBegin
  1346. #undef rEnd
  1347. /******************************************************************************
  1348. * MiddleWare Implementation *
  1349. ******************************************************************************/
  1350. CarlaMiddleWareImpl::CarlaMiddleWareImpl(MiddleWare *mw, SYNTH_T synth_,
  1351. CarlaConfig* config, int preferrred_port)
  1352. :parent(mw), config(config), ui(nullptr), synth(std::move(synth_)),
  1353. presetsstore(*config), autoSave(-1, [this]() {
  1354. auto master = this->master;
  1355. this->doReadOnlyOp([master](){
  1356. std::string home = getenv("HOME");
  1357. std::string save_file = home+"/.local/zynaddsubfx-"+to_s(getpid())+"-autosave.xmz";
  1358. printf("doing an autosave <%s>...\n", save_file.c_str());
  1359. int res = master->saveXML(save_file.c_str());
  1360. (void)res;});})
  1361. {
  1362. bToU = new rtosc::ThreadLink(4096*2*16,1024/16);
  1363. uToB = new rtosc::ThreadLink(4096*2*16,1024/16);
  1364. midi_mapper.base_ports = &Master::ports;
  1365. midi_mapper.rt_cb = [this](const char *msg){handleMsg(msg);};
  1366. if(preferrred_port != -1)
  1367. server = lo_server_new_with_proto(to_s(preferrred_port).c_str(),
  1368. LO_UDP, liblo_error_cb);
  1369. else
  1370. server = lo_server_new_with_proto(NULL, LO_UDP, liblo_error_cb);
  1371. if(server) {
  1372. lo_server_add_method(server, NULL, NULL, handler_function, mw);
  1373. fprintf(stderr, "lo server running on %d\n", lo_server_get_port(server));
  1374. } else
  1375. fprintf(stderr, "lo server could not be started :-/\n");
  1376. //dummy callback for starters
  1377. cb = [](void*, const char*){};
  1378. idle = 0;
  1379. idle_ptr = 0;
  1380. master = new Master(synth, config);
  1381. master->bToU = bToU;
  1382. master->uToB = uToB;
  1383. osc = GUI::genOscInterface(mw);
  1384. //Grab objects of interest from master
  1385. updateResources(master);
  1386. //Null out Load IDs
  1387. for(int i=0; i < NUM_MIDI_PARTS; ++i) {
  1388. pending_load[i] = 0;
  1389. actual_load[i] = 0;
  1390. }
  1391. //Setup Undo
  1392. undo.setCallback([this](const char *msg) {
  1393. // printf("undo callback <%s>\n", msg);
  1394. char buf[1024];
  1395. rtosc_message(buf, 1024, "/undo_pause","");
  1396. handleMsg(buf);
  1397. handleMsg(msg);
  1398. rtosc_message(buf, 1024, "/undo_resume","");
  1399. handleMsg(buf);
  1400. });
  1401. //Setup starting time
  1402. struct timespec time;
  1403. clock_gettime(CLOCK_MONOTONIC, &time);
  1404. start_time_sec = time.tv_sec;
  1405. start_time_nsec = time.tv_nsec;
  1406. offline = false;
  1407. }
  1408. CarlaMiddleWareImpl::~CarlaMiddleWareImpl(void)
  1409. {
  1410. if(server)
  1411. lo_server_free(server);
  1412. delete master;
  1413. delete osc;
  1414. delete bToU;
  1415. delete uToB;
  1416. }
  1417. /** Threading When Saving
  1418. * ----------------------
  1419. *
  1420. * Procedure Middleware:
  1421. * 1) Middleware sends /freeze_state to backend
  1422. * 2) Middleware waits on /state_frozen from backend
  1423. * All intervening commands are held for out of order execution
  1424. * 3) Aquire memory
  1425. * At this time by the memory barrier we are guarenteed that all old
  1426. * writes are done and assuming the freezing logic is sound, then it is
  1427. * impossible for any other parameter to change at this time
  1428. * 3) Middleware performs saving operation
  1429. * 4) Middleware sends /thaw_state to backend
  1430. * 5) Restore in order execution
  1431. *
  1432. * Procedure Backend:
  1433. * 1) Observe /freeze_state and disable all mutating events (MIDI CC)
  1434. * 2) Run a memory release to ensure that all writes are complete
  1435. * 3) Send /state_frozen to Middleware
  1436. * time...
  1437. * 4) Observe /thaw_state and resume normal processing
  1438. */
  1439. void CarlaMiddleWareImpl::doReadOnlyOp(std::function<void()> read_only_fn)
  1440. {
  1441. assert(uToB);
  1442. uToB->write("/freeze_state","");
  1443. std::list<const char *> fico;
  1444. int tries = 0;
  1445. while(tries++ < 10000) {
  1446. if(!bToU->hasNext()) {
  1447. usleep(500);
  1448. continue;
  1449. }
  1450. const char *msg = bToU->read();
  1451. if(!strcmp("/state_frozen", msg))
  1452. break;
  1453. size_t bytes = rtosc_message_length(msg, bToU->buffer_size());
  1454. char *save_buf = new char[bytes];
  1455. memcpy(save_buf, msg, bytes);
  1456. fico.push_back(save_buf);
  1457. }
  1458. assert(tries < 10000);//if this happens, the backend must be dead
  1459. std::atomic_thread_fence(std::memory_order_acquire);
  1460. //Now it is safe to do any read only operation
  1461. read_only_fn();
  1462. //Now to resume normal operations
  1463. uToB->write("/thaw_state","");
  1464. for(auto x:fico) {
  1465. uToB->raw_write(x);
  1466. delete [] x;
  1467. }
  1468. }
  1469. //Offline detection code:
  1470. // - Assume that the audio callback should be run at least once every 50ms
  1471. // - Atomically provide the number of ms since start to Master
  1472. // - Every time middleware ticks provide a heart beat
  1473. // - If when the heart beat is provided the backend is more than 200ms behind
  1474. // the last heartbeat then it must be offline
  1475. // - When marked offline the backend doesn't receive another heartbeat until it
  1476. // registers the current beat that it's behind on
  1477. void CarlaMiddleWareImpl::heartBeat(Master *master)
  1478. {
  1479. //Current time
  1480. //Last provided beat
  1481. //Last acknowledged beat
  1482. //Current offline status
  1483. struct timespec time;
  1484. clock_gettime(CLOCK_MONOTONIC, &time);
  1485. uint32_t now = (time.tv_sec-start_time_sec)*100 +
  1486. (time.tv_nsec-start_time_nsec)*1e-9*100;
  1487. int32_t last_ack = master->last_ack;
  1488. int32_t last_beat = master->last_beat;
  1489. //everything is considered online for the first second
  1490. if(now < 100)
  1491. return;
  1492. if(offline) {
  1493. if(last_beat == last_ack) {
  1494. //XXX INSERT MESSAGE HERE ABOUT TRANSITION TO ONLINE
  1495. offline = false;
  1496. //Send new heart beat
  1497. master->last_beat = now;
  1498. }
  1499. } else {
  1500. //it's unquestionably alive
  1501. if(last_beat == last_ack) {
  1502. //Send new heart beat
  1503. master->last_beat = now;
  1504. return;
  1505. }
  1506. //it's pretty likely dead
  1507. if(last_beat-last_ack > 0 && now-last_beat > 20) {
  1508. //The backend has had 200 ms to acquire a new beat
  1509. //The backend instead has an older beat
  1510. //XXX INSERT MESSAGE HERE ABOUT TRANSITION TO OFFLINE
  1511. offline = true;
  1512. return;
  1513. }
  1514. //who knows if it's alive or not here, give it a few ms to acquire or
  1515. //not
  1516. }
  1517. }
  1518. void CarlaMiddleWareImpl::doReadOnlyOpPlugin(std::function<void()> read_only_fn)
  1519. {
  1520. assert(uToB);
  1521. int offline = 0;
  1522. if(offline) {
  1523. std::atomic_thread_fence(std::memory_order_acquire);
  1524. //Now it is safe to do any read only operation
  1525. read_only_fn();
  1526. } else if(!doReadOnlyOpNormal(read_only_fn, true)) {
  1527. //check if we just transitioned to offline mode
  1528. std::atomic_thread_fence(std::memory_order_acquire);
  1529. //Now it is safe to do any read only operation
  1530. read_only_fn();
  1531. }
  1532. }
  1533. bool CarlaMiddleWareImpl::doReadOnlyOpNormal(std::function<void()> read_only_fn, bool canfail)
  1534. {
  1535. assert(uToB);
  1536. uToB->write("/freeze_state","");
  1537. std::list<const char *> fico;
  1538. int tries = 0;
  1539. while(tries++ < 2000) {
  1540. if(!bToU->hasNext()) {
  1541. usleep(500);
  1542. continue;
  1543. }
  1544. const char *msg = bToU->read();
  1545. if(!strcmp("/state_frozen", msg))
  1546. break;
  1547. size_t bytes = rtosc_message_length(msg, bToU->buffer_size());
  1548. char *save_buf = new char[bytes];
  1549. memcpy(save_buf, msg, bytes);
  1550. fico.push_back(save_buf);
  1551. }
  1552. if(canfail) {
  1553. //Now to resume normal operations
  1554. uToB->write("/thaw_state","");
  1555. for(auto x:fico) {
  1556. uToB->raw_write(x);
  1557. delete [] x;
  1558. }
  1559. return false;
  1560. }
  1561. assert(tries < 10000);//if this happens, the backend must be dead
  1562. std::atomic_thread_fence(std::memory_order_acquire);
  1563. //Now it is safe to do any read only operation
  1564. read_only_fn();
  1565. //Now to resume normal operations
  1566. uToB->write("/thaw_state","");
  1567. for(auto x:fico) {
  1568. uToB->raw_write(x);
  1569. delete [] x;
  1570. }
  1571. return true;
  1572. }
  1573. void CarlaMiddleWareImpl::broadcastToRemote(const char *rtmsg)
  1574. {
  1575. //Always send to the local UI
  1576. sendToRemote(rtmsg, "GUI");
  1577. //Send to remote UI if there's one listening
  1578. for(auto rem:known_remotes)
  1579. if(rem != "GUI")
  1580. sendToRemote(rtmsg, rem);
  1581. broadcast = false;
  1582. }
  1583. void CarlaMiddleWareImpl::sendToRemote(const char *rtmsg, std::string dest)
  1584. {
  1585. if(!rtmsg || rtmsg[0] != '/' || !rtosc_message_length(rtmsg, -1)) {
  1586. printf("[Warning] Invalid message in sendToRemote <%s>...\n", rtmsg);
  1587. return;
  1588. }
  1589. //printf("sendToRemote(%s:%s,%s)\n", rtmsg, rtosc_argument_string(rtmsg),
  1590. // dest.c_str());
  1591. if(dest == "GUI") {
  1592. cb(ui, rtmsg);
  1593. } else if(!dest.empty()) {
  1594. lo_message msg = lo_message_deserialise((void*)rtmsg,
  1595. rtosc_message_length(rtmsg, bToU->buffer_size()), NULL);
  1596. if(!msg) {
  1597. printf("[ERROR] OSC to <%s> Failed To Parse In Liblo\n", rtmsg);
  1598. return;
  1599. }
  1600. //Send to known url
  1601. lo_address addr = lo_address_new_from_url(dest.c_str());
  1602. if(addr)
  1603. lo_send_message(addr, rtmsg, msg);
  1604. lo_address_free(addr);
  1605. lo_message_free(msg);
  1606. }
  1607. }
  1608. /**
  1609. * Handle all events coming from the backend
  1610. *
  1611. * This includes forwarded events which need to be retransmitted to the backend
  1612. * after the snooping code inspects the message
  1613. */
  1614. void CarlaMiddleWareImpl::bToUhandle(const char *rtmsg)
  1615. {
  1616. //Verify Message isn't a known corruption bug
  1617. assert(strcmp(rtmsg, "/part0/kit0/Ppadenableda"));
  1618. assert(strcmp(rtmsg, "/ze_state"));
  1619. //Dump Incomming Events For Debugging
  1620. if(strcmp(rtmsg, "/vu-meter") && false) {
  1621. fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 1 + 30, 0 + 40);
  1622. fprintf(stdout, "frontend[%c]: '%s'<%s>\n", forward?'f':broadcast?'b':'N',
  1623. rtmsg, rtosc_argument_string(rtmsg));
  1624. fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  1625. }
  1626. //Activity dot
  1627. //printf(".");fflush(stdout);
  1628. MwDataObj d(this);
  1629. middlewareReplyPorts.dispatch(rtmsg, d, true);
  1630. if(!rtmsg) {
  1631. fprintf(stderr, "[ERROR] Unexpected Null OSC In Zyn\n");
  1632. return;
  1633. }
  1634. in_order = true;
  1635. //Normal message not captured by the ports
  1636. if(d.matches == 0) {
  1637. if(forward) {
  1638. forward = false;
  1639. handleMsg(rtmsg);
  1640. } if(broadcast)
  1641. broadcastToRemote(rtmsg);
  1642. else
  1643. sendToCurrentRemote(rtmsg);
  1644. }
  1645. in_order = false;
  1646. }
  1647. //Allocate kits on a as needed basis
  1648. void CarlaMiddleWareImpl::kitEnable(const char *msg)
  1649. {
  1650. const string argv = rtosc_argument_string(msg);
  1651. if(argv != "T")
  1652. return;
  1653. //Extract fields from:
  1654. //BASE/part#/kit#/Pxxxenabled
  1655. int type = -1;
  1656. if(strstr(msg, "Padenabled"))
  1657. type = 0;
  1658. else if(strstr(msg, "Ppadenabled"))
  1659. type = 1;
  1660. else if(strstr(msg, "Psubenabled"))
  1661. type = 2;
  1662. else
  1663. return;
  1664. const char *tmp = strstr(msg, "part");
  1665. if(tmp == NULL)
  1666. return;
  1667. const int part = atoi(tmp+4);
  1668. tmp = strstr(msg, "kit");
  1669. if(tmp == NULL)
  1670. return;
  1671. const int kit = atoi(tmp+3);
  1672. kitEnable(part, kit, type);
  1673. }
  1674. void CarlaMiddleWareImpl::kitEnable(int part, int kit, int type)
  1675. {
  1676. //printf("attempting a kit enable<%d,%d,%d>\n", part, kit, type);
  1677. string url = "/part"+to_s(part)+"/kit"+to_s(kit)+"/";
  1678. void *ptr = NULL;
  1679. if(type == 0 && kits.add[part][kit] == NULL) {
  1680. ptr = kits.add[part][kit] = new ADnoteParameters(synth, master->fft,
  1681. &master->time);
  1682. url += "adpars-data";
  1683. obj_store.extractAD(kits.add[part][kit], part, kit);
  1684. } else if(type == 1 && kits.pad[part][kit] == NULL) {
  1685. ptr = kits.pad[part][kit] = new PADnoteParameters(synth, master->fft,
  1686. &master->time);
  1687. url += "padpars-data";
  1688. obj_store.extractPAD(kits.pad[part][kit], part, kit);
  1689. } else if(type == 2 && kits.sub[part][kit] == NULL) {
  1690. ptr = kits.sub[part][kit] = new SUBnoteParameters(&master->time);
  1691. url += "subpars-data";
  1692. }
  1693. //Send the new memory
  1694. if(ptr)
  1695. uToB->write(url.c_str(), "b", sizeof(void*), &ptr);
  1696. }
  1697. /*
  1698. * Handle all messages traveling to the realtime side.
  1699. */
  1700. void CarlaMiddleWareImpl::handleMsg(const char *msg)
  1701. {
  1702. //Check for known bugs
  1703. assert(msg && *msg && strrchr(msg, '/')[1]);
  1704. assert(strstr(msg,"free") == NULL || strstr(rtosc_argument_string(msg), "b") == NULL);
  1705. assert(strcmp(msg, "/part0/Psysefxvol"));
  1706. assert(strcmp(msg, "/Penabled"));
  1707. assert(strcmp(msg, "part0/part0/Ppanning"));
  1708. assert(strcmp(msg, "sysefx0sysefx0/preset"));
  1709. assert(strcmp(msg, "/sysefx0preset"));
  1710. assert(strcmp(msg, "Psysefxvol0/part0"));
  1711. if(strcmp("/get-vu", msg) && false) {
  1712. fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 6 + 30, 0 + 40);
  1713. fprintf(stdout, "middleware: '%s':%s\n", msg, rtosc_argument_string(msg));
  1714. fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  1715. }
  1716. const char *last_path = strrchr(msg, '/');
  1717. if(!last_path) {
  1718. printf("Bad message in handleMsg() <%s>\n", msg);
  1719. assert(false);
  1720. return;
  1721. }
  1722. MwDataObj d(this);
  1723. middwareSnoopPorts.dispatch(msg, d, true);
  1724. //A message unmodified by snooping
  1725. if(d.matches == 0 || d.forwarded) {
  1726. //if(strcmp("/get-vu", msg)) {
  1727. // printf("Message Continuing on<%s:%s>...\n", msg, rtosc_argument_string(msg));
  1728. //}
  1729. uToB->raw_write(msg);
  1730. } else {
  1731. //printf("Message Handled<%s:%s>...\n", msg, rtosc_argument_string(msg));
  1732. }
  1733. }
  1734. void CarlaMiddleWareImpl::write(const char *path, const char *args, ...)
  1735. {
  1736. //We have a free buffer in the threadlink, so use it
  1737. va_list va;
  1738. va_start(va, args);
  1739. write(path, args, va);
  1740. va_end(va);
  1741. }
  1742. void CarlaMiddleWareImpl::write(const char *path, const char *args, va_list va)
  1743. {
  1744. //printf("is that a '%s' I see there?\n", path);
  1745. char *buffer = uToB->buffer();
  1746. unsigned len = uToB->buffer_size();
  1747. bool success = rtosc_vmessage(buffer, len, path, args, va);
  1748. //printf("working on '%s':'%s'\n",path, args);
  1749. if(success)
  1750. handleMsg(buffer);
  1751. else
  1752. warnx("Failed to write message to '%s'", path);
  1753. }
  1754. /******************************************************************************
  1755. * MidleWare Forwarding Stubs *
  1756. ******************************************************************************/
  1757. MiddleWare::MiddleWare(SYNTH_T synth, CarlaConfig* config,
  1758. int preferred_port)
  1759. :impl(new CarlaMiddleWareImpl(this, std::move(synth), config, preferred_port))
  1760. {}
  1761. MiddleWare::~MiddleWare(void)
  1762. {
  1763. delete impl;
  1764. }
  1765. void MiddleWare::updateResources(Master *m)
  1766. {
  1767. impl->updateResources(m);
  1768. }
  1769. Master *MiddleWare::spawnMaster(void)
  1770. {
  1771. assert(impl->master);
  1772. assert(impl->master->uToB);
  1773. return impl->master;
  1774. }
  1775. void MiddleWare::enableAutoSave(int interval_sec)
  1776. {
  1777. impl->autoSave.dt = interval_sec;
  1778. }
  1779. int MiddleWare::checkAutoSave(void)
  1780. {
  1781. //save spec zynaddsubfx-PID-autosave.xmz
  1782. const std::string home = getenv("HOME");
  1783. const std::string save_dir = home+"/.local/";
  1784. DIR *dir = opendir(save_dir.c_str());
  1785. if(dir == NULL)
  1786. return -1;
  1787. struct dirent *fn;
  1788. int reload_save = -1;
  1789. while((fn = readdir(dir))) {
  1790. const char *filename = fn->d_name;
  1791. const char *prefix = "zynaddsubfx-";
  1792. //check for manditory prefix
  1793. if(strstr(filename, prefix) != filename)
  1794. continue;
  1795. int id = atoi(filename+strlen(prefix));
  1796. bool in_use = false;
  1797. std::string proc_file = "/proc/" + to_s(id) + "/comm";
  1798. std::ifstream ifs(proc_file);
  1799. if(ifs.good()) {
  1800. std::string comm_name;
  1801. ifs >> comm_name;
  1802. in_use = (comm_name == "zynaddsubfx");
  1803. }
  1804. if(!in_use) {
  1805. reload_save = id;
  1806. break;
  1807. }
  1808. }
  1809. closedir(dir);
  1810. return reload_save;
  1811. }
  1812. void MiddleWare::removeAutoSave(void)
  1813. {
  1814. std::string home = getenv("HOME");
  1815. std::string save_file = home+"/.local/zynaddsubfx-"+to_s(getpid())+"-autosave.xmz";
  1816. remove(save_file.c_str());
  1817. }
  1818. Fl_Osc_Interface *MiddleWare::spawnUiApi(void)
  1819. {
  1820. return impl->osc;
  1821. }
  1822. void MiddleWare::tick(void)
  1823. {
  1824. impl->tick();
  1825. }
  1826. void MiddleWare::doReadOnlyOp(std::function<void()> fn)
  1827. {
  1828. impl->doReadOnlyOp(fn);
  1829. }
  1830. void MiddleWare::setUiCallback(void(*cb)(void*,const char *), void *ui)
  1831. {
  1832. impl->cb = cb;
  1833. impl->ui = ui;
  1834. }
  1835. void MiddleWare::setIdleCallback(void(*cb)(void*), void *ptr)
  1836. {
  1837. impl->idle = cb;
  1838. impl->idle_ptr = ptr;
  1839. }
  1840. void MiddleWare::transmitMsg(const char *msg)
  1841. {
  1842. impl->handleMsg(msg);
  1843. }
  1844. void MiddleWare::transmitMsg(const char *path, const char *args, ...)
  1845. {
  1846. char buffer[1024];
  1847. va_list va;
  1848. va_start(va,args);
  1849. if(rtosc_vmessage(buffer,1024,path,args,va))
  1850. transmitMsg(buffer);
  1851. else
  1852. fprintf(stderr, "Error in transmitMsg(...)\n");
  1853. va_end(va);
  1854. }
  1855. void MiddleWare::transmitMsg_va(const char *path, const char *args, va_list va)
  1856. {
  1857. char buffer[1024];
  1858. if(rtosc_vmessage(buffer, 1024, path, args, va))
  1859. transmitMsg(buffer);
  1860. else
  1861. fprintf(stderr, "Error in transmitMsg(va)n");
  1862. }
  1863. void MiddleWare::messageAnywhere(const char *path, const char *args, ...)
  1864. {
  1865. auto *mem = impl->multi_thread_source.alloc();
  1866. if(!mem)
  1867. fprintf(stderr, "Middleware::messageAnywhere memory pool out of memory...\n");
  1868. va_list va;
  1869. va_start(va,args);
  1870. if(rtosc_vmessage(mem->memory,mem->size,path,args,va))
  1871. impl->multi_thread_source.write(mem);
  1872. else {
  1873. fprintf(stderr, "Middleware::messageAnywhere message too big...\n");
  1874. impl->multi_thread_source.free(mem);
  1875. }
  1876. }
  1877. void MiddleWare::pendingSetBank(int bank)
  1878. {
  1879. impl->bToU->write("/setbank", "c", bank);
  1880. }
  1881. void MiddleWare::pendingSetProgram(int part, int program)
  1882. {
  1883. impl->pending_load[part]++;
  1884. impl->bToU->write("/setprogram", "cc", part, program);
  1885. }
  1886. std::string MiddleWare::activeUrl(void)
  1887. {
  1888. return impl->last_url;
  1889. }
  1890. void MiddleWare::activeUrl(std::string u)
  1891. {
  1892. impl->last_url = u;
  1893. }
  1894. const SYNTH_T &MiddleWare::getSynth(void) const
  1895. {
  1896. return impl->synth;
  1897. }
  1898. const char* MiddleWare::getServerAddress(void) const
  1899. {
  1900. if(impl->server)
  1901. return lo_server_get_url(impl->server);
  1902. else
  1903. return "NULL";
  1904. }
  1905. const PresetsStore& MiddleWare::getPresetsStore() const
  1906. {
  1907. return impl->presetsstore;
  1908. }
  1909. PresetsStore& MiddleWare::getPresetsStore()
  1910. {
  1911. return impl->presetsstore;
  1912. }