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.

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