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.

2139 lines
66KB

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