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.

1558 lines
49KB

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