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.

1860 lines
58KB

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