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.

1649 lines
51KB

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