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