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.

1736 lines
54KB

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