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.

1219 lines
38KB

  1. /*
  2. ZynAddSubFX - a software synthesizer
  3. Master.cpp - It sends Midi Messages to Parts, receives samples from parts,
  4. process them with system/insertion effects and mix them
  5. Copyright (C) 2002-2005 Nasca Octavian Paul
  6. Author: Nasca Octavian Paul
  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 "Master.h"
  13. #include "Part.h"
  14. #include "../Misc/Stereo.h"
  15. #include "../Misc/Util.h"
  16. #include "../Params/LFOParams.h"
  17. #include "../Effects/EffectMgr.h"
  18. #include "../DSP/FFTwrapper.h"
  19. #include "../Misc/Allocator.h"
  20. #include "../Containers/ScratchString.h"
  21. #include "../Nio/Nio.h"
  22. #include "PresetExtractor.h"
  23. #include <rtosc/ports.h>
  24. #include <rtosc/port-sugar.h>
  25. #include <rtosc/thread-link.h>
  26. #include <stdio.h>
  27. #include <sys/stat.h>
  28. #include <sys/types.h>
  29. #include <iostream>
  30. #include <algorithm>
  31. #include <cmath>
  32. #include <atomic>
  33. #include <unistd.h>
  34. using namespace std;
  35. using namespace rtosc;
  36. #define rObject Master
  37. static const Ports sysefxPort =
  38. {
  39. {"part#" STRINGIFY(NUM_MIDI_PARTS) "::i", rProp(parameter)
  40. rDoc("gain on part to sysefx routing"), 0,
  41. [](const char *m, RtData&d)
  42. {
  43. //ok, this is going to be an ugly workaround
  44. //we know that if we are here the message previously MUST have
  45. //matched Psysefxvol#/
  46. //and the number is one or two digits at most
  47. const char *index_1 = m;
  48. index_1 -=2;
  49. assert(isdigit(*index_1));
  50. if(isdigit(index_1[-1]))
  51. index_1--;
  52. int ind1 = atoi(index_1);
  53. //Now get the second index like normal
  54. while(!isdigit(*m)) m++;
  55. int ind2 = atoi(m);
  56. Master &mast = *(Master*)d.obj;
  57. if(rtosc_narguments(m)) {
  58. mast.setPsysefxvol(ind2, ind1, rtosc_argument(m,0).i);
  59. d.broadcast(d.loc, "i", mast.Psysefxvol[ind1][ind2]);
  60. } else
  61. d.reply(d.loc, "i", mast.Psysefxvol[ind1][ind2]);
  62. }}
  63. };
  64. static const Ports sysefsendto =
  65. {
  66. {"to#" STRINGIFY(NUM_SYS_EFX) "::i",
  67. rProp(parameter) rDoc("sysefx to sysefx routing gain"), 0, [](const char *m, RtData&d)
  68. {
  69. //same ugly workaround as before
  70. const char *index_1 = m;
  71. index_1 -=2;
  72. assert(isdigit(*index_1));
  73. if(isdigit(index_1[-1]))
  74. index_1--;
  75. int ind1 = atoi(index_1);
  76. //Now get the second index like normal
  77. while(!isdigit(*m)) m++;
  78. int ind2 = atoi(m);
  79. Master &master = *(Master*)d.obj;
  80. if(rtosc_narguments(m))
  81. master.setPsysefxsend(ind1, ind2, rtosc_argument(m,0).i);
  82. else
  83. d.reply(d.loc, "i", master.Psysefxsend[ind1][ind2]);
  84. }}
  85. };
  86. #define rBegin [](const char *msg, RtData &d) { Master *m = (Master*)d.obj
  87. #define rEnd }
  88. static const Ports watchPorts = {
  89. {"add:s", rDoc("Add synthesis state to watch"), 0,
  90. rBegin;
  91. m->watcher.add_watch(rtosc_argument(msg,0).s);
  92. rEnd},
  93. };
  94. extern const Ports bankPorts;
  95. static const Ports master_ports = {
  96. rString(last_xmz, XMZ_PATH_MAX, "File name for last name loaded if any."),
  97. rRecursp(part, 16, "Part"),//NUM_MIDI_PARTS
  98. rRecursp(sysefx, 4, "System Effect"),//NUM_SYS_EFX
  99. rRecursp(insefx, 8, "Insertion Effect"),//NUM_INS_EFX
  100. rRecur(microtonal, "Micrtonal Mapping Functionality"),
  101. rRecur(ctl, "Controller"),
  102. rArrayI(Pinsparts, NUM_INS_EFX, rOpt(-1, Master),
  103. rOptions(Part1, Part2, Part3, Part4, Part5, Part6,
  104. Part7, Part8, Part9, Part10, Part11, Part12,
  105. Part13, Part14, Part15, Part16),
  106. "Part to insert part onto"),
  107. {"Pkeyshift::i", rShort("key shift") rProp(parameter) rLinear(0,127) rDoc("Global Key Shift"), 0, [](const char *m, RtData&d) {
  108. if(rtosc_narguments(m)==0) {
  109. d.reply(d.loc, "i", ((Master*)d.obj)->Pkeyshift);
  110. } else if(rtosc_narguments(m)==1 && rtosc_type(m,0)=='i') {
  111. ((Master*)d.obj)->setPkeyshift(limit<char>(rtosc_argument(m,0).i,0,127));
  112. d.broadcast(d.loc, "i", ((Master*)d.obj)->Pkeyshift);}}},
  113. {"echo", rDoc("Hidden port to echo messages"), 0, [](const char *m, RtData&d) {
  114. d.reply(m-1);}},
  115. {"get-vu:", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) {
  116. Master *m = (Master*)d.obj;
  117. d.reply("/vu-meter", "bb", sizeof(m->vu), &m->vu, sizeof(float)*NUM_MIDI_PARTS, m->vuoutpeakpart);}},
  118. {"vu-meter:", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) {
  119. Master *m = (Master*)d.obj;
  120. char types[6+NUM_MIDI_PARTS+1] = {0};
  121. rtosc_arg_t args[6+NUM_MIDI_PARTS+1];
  122. for(int i=0; i<6+NUM_MIDI_PARTS; ++i)
  123. types[i] = 'f';
  124. args[0].f = m->vu.outpeakl;
  125. args[1].f = m->vu.outpeakr;
  126. args[2].f = m->vu.maxoutpeakl;
  127. args[3].f = m->vu.maxoutpeakr;
  128. args[4].f = m->vu.rmspeakl;
  129. args[5].f = m->vu.rmspeakr;
  130. for(int i=0; i<NUM_MIDI_PARTS; ++i)
  131. args[6+i].f = m->vuoutpeakpart[i];
  132. d.replyArray("/vu-meter", types, args);}},
  133. {"reset-vu:", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) {
  134. Master *m = (Master*)d.obj;
  135. m->vuresetpeaks();}},
  136. {"load-part:ib", rProp(internal) rDoc("Load Part From Middleware"), 0, [](const char *msg, RtData &d) {
  137. Master *m = (Master*)d.obj;
  138. Part *p = *(Part**)rtosc_argument(msg, 1).b.data;
  139. int i = rtosc_argument(msg, 0).i;
  140. m->part[i]->cloneTraits(*p);
  141. m->part[i]->kill_rt();
  142. d.reply("/free", "sb", "Part", sizeof(void*), &m->part[i]);
  143. m->part[i] = p;
  144. p->initialize_rt();
  145. }},
  146. {"active_keys:", rProp("Obtain a list of active notes"), 0,
  147. rBegin;
  148. char keys[129] = {0};
  149. for(int i=0; i<128; ++i)
  150. keys[i] = m->activeNotes[i] ? 'T' : 'F';
  151. d.broadcast(d.loc, keys);
  152. rEnd},
  153. {"Pvolume::i", rShort("volume") rProp(parameter) rLinear(0,127) rDoc("Master Volume"), 0,
  154. [](const char *m, rtosc::RtData &d) {
  155. if(rtosc_narguments(m)==0) {
  156. d.reply(d.loc, "i", ((Master*)d.obj)->Pvolume);
  157. } else if(rtosc_narguments(m)==1 && rtosc_type(m,0)=='i') {
  158. ((Master*)d.obj)->setPvolume(limit<char>(rtosc_argument(m,0).i,0,127));
  159. d.broadcast(d.loc, "i", ((Master*)d.obj)->Pvolume);}}},
  160. {"volume::i", rShort("volume") rProp(parameter) rLinear(0,127) rDoc("Master Volume"), 0,
  161. [](const char *m, rtosc::RtData &d) {
  162. if(rtosc_narguments(m)==0) {
  163. d.reply(d.loc, "i", ((Master*)d.obj)->Pvolume);
  164. } else if(rtosc_narguments(m)==1 && rtosc_type(m,0)=='i') {
  165. ((Master*)d.obj)->setPvolume(limit<char>(rtosc_argument(m,0).i,0,127));
  166. d.broadcast(d.loc, "i", ((Master*)d.obj)->Pvolume);}}},
  167. {"Psysefxvol#" STRINGIFY(NUM_SYS_EFX) "/::i", 0, &sysefxPort,
  168. [](const char *msg, rtosc::RtData &d) {
  169. SNIP;
  170. sysefxPort.dispatch(msg, d);
  171. }},
  172. {"sysefxfrom#" STRINGIFY(NUM_SYS_EFX) "/", rDoc("Routing Between System Effects"), &sysefsendto,
  173. [](const char *msg, RtData&d) {
  174. SNIP;
  175. sysefsendto.dispatch(msg, d);
  176. }},
  177. {"noteOn:iii", rDoc("Noteon Event"), 0,
  178. [](const char *m,RtData &d){
  179. Master *M = (Master*)d.obj;
  180. M->noteOn(rtosc_argument(m,0).i,rtosc_argument(m,1).i,rtosc_argument(m,2).i);}},
  181. {"noteOff:ii", rDoc("Noteoff Event"), 0,
  182. [](const char *m,RtData &d){
  183. Master *M = (Master*)d.obj;
  184. M->noteOff(rtosc_argument(m,0).i,rtosc_argument(m,1).i);}},
  185. {"virtual_midi_cc:iii", rDoc("MIDI CC Event"), 0,
  186. [](const char *m,RtData &d){
  187. Master *M = (Master*)d.obj;
  188. M->setController(rtosc_argument(m,0).i,rtosc_argument(m,1).i,rtosc_argument(m,2).i);}},
  189. {"setController:iii", rDoc("MIDI CC Event"), 0,
  190. [](const char *m,RtData &d){
  191. Master *M = (Master*)d.obj;
  192. M->setController(rtosc_argument(m,0).i,rtosc_argument(m,1).i,rtosc_argument(m,2).i);}},
  193. {"Panic:", rDoc("Stop all sound"), 0,
  194. [](const char *, RtData &d) {
  195. Master &M = *(Master*)d.obj;
  196. M.ShutUp();
  197. }},
  198. {"freeze_state:", rProp(internal) rDoc("Disable OSC event handling\n"
  199. "This sets up a read-only mode from which it's safe for another"
  200. " thread to save parameters"), 0,
  201. [](const char *,RtData &d) {
  202. Master *M = (Master*)d.obj;
  203. std::atomic_thread_fence(std::memory_order_release);
  204. M->frozenState = true;
  205. d.reply("/state_frozen", "");}},
  206. {"thaw_state:", rProp(internal) rDoc("Resume handling OSC messages\n"
  207. "See /freeze_state for more information"), 0,
  208. [](const char *,RtData &d) {
  209. Master *M = (Master*)d.obj;
  210. M->frozenState = false;}},
  211. {"midi-learn/", 0, &rtosc::MidiMapperRT::ports,
  212. [](const char *msg, RtData &d) {
  213. Master *M = (Master*)d.obj;
  214. SNIP;
  215. printf("residue message = <%s>\n", msg);
  216. d.obj = &M->midi;
  217. rtosc::MidiMapperRT::ports.dispatch(msg,d);}},
  218. {"close-ui:", rDoc("Request to close any connection named \"GUI\""), 0,
  219. [](const char *, RtData &d) {
  220. d.reply("/close-ui", "");}},
  221. {"add-rt-memory:bi", rProp(internal) rDoc("Add Additional Memory To RT MemPool"), 0,
  222. [](const char *msg, RtData &d)
  223. {
  224. Master &m = *(Master*)d.obj;
  225. char *mem = *(char**)rtosc_argument(msg, 0).b.data;
  226. int i = rtosc_argument(msg, 1).i;
  227. m.memory->addMemory(mem, i);
  228. m.pendingMemory = false;
  229. }},
  230. {"samplerate:", rMap(unit, Hz) rDoc("Get synthesizer sample rate"), 0, [](const char *, RtData &d) {
  231. Master &m = *(Master*)d.obj;
  232. d.reply("/samplerate", "f", m.synth.samplerate_f);
  233. }},
  234. {"oscilsize:", rDoc("Get synthesizer oscillator size"), 0, [](const char *, RtData &d) {
  235. Master &m = *(Master*)d.obj;
  236. d.reply("/oscilsize", "f", m.synth.oscilsize_f);
  237. d.reply("/oscilsize", "i", m.synth.oscilsize);
  238. }},
  239. {"undo_pause:",rProp(internal) rDoc("pause undo event recording"),0,
  240. [](const char *, rtosc::RtData &d) {d.reply("/undo_pause", "");}},
  241. {"undo_resume:",rProp(internal) rDoc("resume undo event recording"),0,
  242. [](const char *, rtosc::RtData &d) {d.reply("/undo_resume", "");}},
  243. {"config/", rDoc("Top Level Application Configuration Parameters"), &Config::ports,
  244. [](const char *, rtosc::RtData &d){d.forward();}},
  245. {"presets/", rDoc("Parameter Presets"), &preset_ports, rBOIL_BEGIN
  246. SNIP
  247. preset_ports.dispatch(msg, data);
  248. rBOIL_END},
  249. {"HDDRecorder/preparefile:s", rDoc("Init WAV file"), 0, [](const char *msg, RtData &d) {
  250. Master *m = (Master*)d.obj;
  251. m->HDDRecorder.preparefile(rtosc_argument(msg, 0).s, 1);}},
  252. {"HDDRecorder/start:", rDoc("Start recording"), 0, [](const char *, RtData &d) {
  253. Master *m = (Master*)d.obj;
  254. m->HDDRecorder.start();}},
  255. {"HDDRecorder/stop:", rDoc("Stop recording"), 0, [](const char *, RtData &d) {
  256. Master *m = (Master*)d.obj;
  257. m->HDDRecorder.stop();}},
  258. {"HDDRecorder/pause:", rDoc("Pause recording"), 0, [](const char *, RtData &d) {
  259. Master *m = (Master*)d.obj;
  260. m->HDDRecorder.pause();}},
  261. {"watch/", rDoc("Interface to grab out live synthesis state"), &watchPorts,
  262. rBOIL_BEGIN;
  263. SNIP;
  264. watchPorts.dispatch(msg, data);
  265. rBOIL_END},
  266. {"bank/", rDoc("Controls for instrument banks"), &bankPorts,
  267. [](const char*,RtData&) {}},
  268. };
  269. #undef rBegin
  270. #undef rEnd
  271. const Ports &Master::ports = master_ports;
  272. class DataObj:public rtosc::RtData
  273. {
  274. public:
  275. DataObj(char *loc_, size_t loc_size_, void *obj_, rtosc::ThreadLink *bToU_)
  276. {
  277. memset(loc_, 0, loc_size_);
  278. loc = loc_;
  279. loc_size = loc_size_;
  280. obj = obj_;
  281. bToU = bToU_;
  282. forwarded = false;
  283. }
  284. virtual void replyArray(const char *path, const char *args, rtosc_arg_t *vals) override
  285. {
  286. char *buffer = bToU->buffer();
  287. rtosc_amessage(buffer,bToU->buffer_size(),path,args,vals);
  288. reply(buffer);
  289. }
  290. virtual void reply(const char *path, const char *args, ...) override
  291. {
  292. va_list va;
  293. va_start(va,args);
  294. char *buffer = bToU->buffer();
  295. rtosc_vmessage(buffer,bToU->buffer_size(),path,args,va);
  296. reply(buffer);
  297. va_end(va);
  298. }
  299. virtual void reply(const char *msg) override
  300. {
  301. if(rtosc_message_length(msg, -1) == 0)
  302. fprintf(stderr, "Warning: Invalid Rtosc message '%s'\n", msg);
  303. bToU->raw_write(msg);
  304. }
  305. virtual void broadcast(const char *path, const char *args, ...) override{
  306. va_list va;
  307. va_start(va,args);
  308. reply("/broadcast", "");
  309. char *buffer = bToU->buffer();
  310. rtosc_vmessage(buffer,bToU->buffer_size(),path,args,va);
  311. reply(buffer);
  312. va_end(va);
  313. }
  314. virtual void broadcast(const char *msg) override
  315. {
  316. reply("/broadcast", "");
  317. reply(msg);
  318. }
  319. virtual void forward(const char *reason) override
  320. {
  321. assert(message);
  322. reply("/forward", "");
  323. printf("forwarding '%s'\n", message);
  324. forwarded = true;
  325. }
  326. bool forwarded;
  327. private:
  328. rtosc::ThreadLink *bToU;
  329. };
  330. vuData::vuData(void)
  331. :outpeakl(0.0f), outpeakr(0.0f), maxoutpeakl(0.0f), maxoutpeakr(0.0f),
  332. rmspeakl(0.0f), rmspeakr(0.0f), clipped(0)
  333. {}
  334. Master::Master(const SYNTH_T &synth_, Config* config)
  335. :HDDRecorder(synth_), time(synth_), ctl(synth_, &time),
  336. microtonal(config->cfg.GzipCompression), bank(config),
  337. frozenState(false), pendingMemory(false),
  338. synth(synth_), gzip_compression(config->cfg.GzipCompression)
  339. {
  340. bToU = NULL;
  341. uToB = NULL;
  342. //Setup MIDI
  343. midi.frontend = [this](const char *msg) {bToU->raw_write(msg);};
  344. midi.backend = [this](const char *msg) {applyOscEvent(msg);};
  345. memory = new AllocatorClass();
  346. swaplr = 0;
  347. off = 0;
  348. smps = 0;
  349. bufl = new float[synth.buffersize];
  350. bufr = new float[synth.buffersize];
  351. last_xmz[0] = 0;
  352. fft = new FFTwrapper(synth.oscilsize);
  353. shutup = 0;
  354. for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
  355. vuoutpeakpart[npart] = 1e-9;
  356. fakepeakpart[npart] = 0;
  357. }
  358. ScratchString ss;
  359. for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
  360. part[npart] = new Part(*memory, synth, time, config->cfg.GzipCompression,
  361. config->cfg.Interpolation, &microtonal, fft, &watcher,
  362. (ss+"/part"+npart+"/").c_str);
  363. //Insertion Effects init
  364. for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
  365. insefx[nefx] = new EffectMgr(*memory, synth, 1, &time);
  366. //System Effects init
  367. for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx)
  368. sysefx[nefx] = new EffectMgr(*memory, synth, 0, &time);
  369. //Note Visualization
  370. for(int i=0; i<128; ++i)
  371. activeNotes[i] = 0;
  372. defaults();
  373. mastercb = 0;
  374. mastercb_ptr = 0;
  375. }
  376. void Master::applyOscEvent(const char *msg)
  377. {
  378. char loc_buf[1024];
  379. DataObj d{loc_buf, 1024, this, bToU};
  380. memset(loc_buf, 0, sizeof(loc_buf));
  381. d.matches = 0;
  382. if(strcmp(msg, "/get-vu") && false) {
  383. fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 5 + 30, 0 + 40);
  384. fprintf(stdout, "backend[*]: '%s'<%s>\n", msg,
  385. rtosc_argument_string(msg));
  386. fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  387. }
  388. ports.dispatch(msg, d, true);
  389. if(d.matches == 0 && !d.forwarded)
  390. fprintf(stderr, "Unknown path '%s:%s'\n", msg, rtosc_argument_string(msg));
  391. if(d.forwarded)
  392. bToU->raw_write(msg);
  393. }
  394. void Master::defaults()
  395. {
  396. volume = 1.0f;
  397. setPvolume(80);
  398. setPkeyshift(64);
  399. for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
  400. part[npart]->defaults();
  401. part[npart]->Prcvchn = npart % NUM_MIDI_CHANNELS;
  402. }
  403. partonoff(0, 1); //enable the first part
  404. for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) {
  405. insefx[nefx]->defaults();
  406. Pinsparts[nefx] = -1;
  407. }
  408. //System Effects init
  409. for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) {
  410. sysefx[nefx]->defaults();
  411. for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
  412. setPsysefxvol(npart, nefx, 0);
  413. for(int nefxto = 0; nefxto < NUM_SYS_EFX; ++nefxto)
  414. setPsysefxsend(nefx, nefxto, 0);
  415. }
  416. microtonal.defaults();
  417. ShutUp();
  418. }
  419. /*
  420. * Note On Messages (velocity=0 for NoteOff)
  421. */
  422. void Master::noteOn(char chan, char note, char velocity)
  423. {
  424. if(velocity) {
  425. for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
  426. if(chan == part[npart]->Prcvchn) {
  427. fakepeakpart[npart] = velocity * 2;
  428. if(part[npart]->Penabled)
  429. part[npart]->NoteOn(note, velocity, keyshift);
  430. }
  431. }
  432. activeNotes[(int)note] = 1;
  433. }
  434. else
  435. this->noteOff(chan, note);
  436. HDDRecorder.triggernow();
  437. }
  438. /*
  439. * Note Off Messages
  440. */
  441. void Master::noteOff(char chan, char note)
  442. {
  443. for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
  444. if((chan == part[npart]->Prcvchn) && part[npart]->Penabled)
  445. part[npart]->NoteOff(note);
  446. activeNotes[(int)note] = 0;
  447. }
  448. /*
  449. * Pressure Messages (velocity=0 for NoteOff)
  450. */
  451. void Master::polyphonicAftertouch(char chan, char note, char velocity)
  452. {
  453. if(velocity) {
  454. for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
  455. if(chan == part[npart]->Prcvchn)
  456. if(part[npart]->Penabled)
  457. part[npart]->PolyphonicAftertouch(note, velocity, keyshift);
  458. }
  459. else
  460. this->noteOff(chan, note);
  461. }
  462. /*
  463. * Controllers
  464. */
  465. void Master::setController(char chan, int type, int par)
  466. {
  467. if(frozenState)
  468. return;
  469. //TODO add chan back
  470. midi.handleCC(type,par);
  471. if((type == C_dataentryhi) || (type == C_dataentrylo)
  472. || (type == C_nrpnhi) || (type == C_nrpnlo)) { //Process RPN and NRPN by the Master (ignore the chan)
  473. ctl.setparameternumber(type, par);
  474. int parhi = -1, parlo = -1, valhi = -1, vallo = -1;
  475. if(ctl.getnrpn(&parhi, &parlo, &valhi, &vallo) == 0) { //this is NRPN
  476. switch(parhi) {
  477. case 0x04: //System Effects
  478. if(parlo < NUM_SYS_EFX)
  479. sysefx[parlo]->seteffectparrt(valhi, vallo);
  480. break;
  481. case 0x08: //Insertion Effects
  482. if(parlo < NUM_INS_EFX)
  483. insefx[parlo]->seteffectparrt(valhi, vallo);
  484. break;
  485. }
  486. }
  487. } else { //other controllers
  488. for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) //Send the controller to all part assigned to the channel
  489. if((chan == part[npart]->Prcvchn) && (part[npart]->Penabled != 0))
  490. part[npart]->SetController(type, par);
  491. if(type == C_allsoundsoff) { //cleanup insertion/system FX
  492. for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx)
  493. sysefx[nefx]->cleanup();
  494. for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
  495. insefx[nefx]->cleanup();
  496. }
  497. }
  498. }
  499. void Master::vuUpdate(const float *outl, const float *outr)
  500. {
  501. //Peak computation (for vumeters)
  502. vu.outpeakl = 1e-12;
  503. vu.outpeakr = 1e-12;
  504. for(int i = 0; i < synth.buffersize; ++i) {
  505. if(fabs(outl[i]) > vu.outpeakl)
  506. vu.outpeakl = fabs(outl[i]);
  507. if(fabs(outr[i]) > vu.outpeakr)
  508. vu.outpeakr = fabs(outr[i]);
  509. }
  510. if((vu.outpeakl > 1.0f) || (vu.outpeakr > 1.0f))
  511. vu.clipped = 1;
  512. if(vu.maxoutpeakl < vu.outpeakl)
  513. vu.maxoutpeakl = vu.outpeakl;
  514. if(vu.maxoutpeakr < vu.outpeakr)
  515. vu.maxoutpeakr = vu.outpeakr;
  516. //RMS Peak computation (for vumeters)
  517. vu.rmspeakl = 1e-12;
  518. vu.rmspeakr = 1e-12;
  519. for(int i = 0; i < synth.buffersize; ++i) {
  520. vu.rmspeakl += outl[i] * outl[i];
  521. vu.rmspeakr += outr[i] * outr[i];
  522. }
  523. vu.rmspeakl = sqrt(vu.rmspeakl / synth.buffersize_f);
  524. vu.rmspeakr = sqrt(vu.rmspeakr / synth.buffersize_f);
  525. //Part Peak computation (for Part vumeters or fake part vumeters)
  526. for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
  527. vuoutpeakpart[npart] = 1.0e-12f;
  528. if(part[npart]->Penabled != 0) {
  529. float *outl = part[npart]->partoutl,
  530. *outr = part[npart]->partoutr;
  531. for(int i = 0; i < synth.buffersize; ++i) {
  532. float tmp = fabs(outl[i] + outr[i]);
  533. if(tmp > vuoutpeakpart[npart])
  534. vuoutpeakpart[npart] = tmp;
  535. }
  536. vuoutpeakpart[npart] *= volume;
  537. }
  538. else
  539. if(fakepeakpart[npart] > 1)
  540. fakepeakpart[npart]--;
  541. }
  542. }
  543. /*
  544. * Enable/Disable a part
  545. */
  546. void Master::partonoff(int npart, int what)
  547. {
  548. if(npart >= NUM_MIDI_PARTS)
  549. return;
  550. if(what == 0) { //disable part
  551. fakepeakpart[npart] = 0;
  552. part[npart]->Penabled = 0;
  553. part[npart]->cleanup();
  554. for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) {
  555. if(Pinsparts[nefx] == npart)
  556. insefx[nefx]->cleanup();
  557. }
  558. }
  559. else { //enabled
  560. part[npart]->Penabled = 1;
  561. fakepeakpart[npart] = 0;
  562. }
  563. }
  564. void Master::setMasterChangedCallback(void(*cb)(void*,Master*), void *ptr)
  565. {
  566. mastercb = cb;
  567. mastercb_ptr = ptr;
  568. }
  569. #if 0
  570. template <class T>
  571. struct def_skip
  572. {
  573. static void skip(const char*& argptr) { argptr += sizeof(T); }
  574. };
  575. template <class T>
  576. struct str_skip
  577. {
  578. static void skip(const char*& argptr) { while(argptr++); /*TODO: 4 padding */ }
  579. };
  580. template<class T, class Display = T, template<class TMP> class SkipsizeFunc = def_skip>
  581. void _dump_prim_arg(const char*& argptr, std::ostream& os)
  582. {
  583. os << ' ' << (Display)*(const T*)argptr;
  584. SkipsizeFunc<T>::skip(argptr);
  585. }
  586. void dump_msg(const char* ptr, std::ostream& os = std::cerr)
  587. {
  588. assert(*ptr == '/');
  589. os << ptr;
  590. while(*++ptr) ; // skip address
  591. while(!*++ptr) ; // skip 0s
  592. assert(*ptr == ',');
  593. os << ' ' << (ptr + 1);
  594. const char* argptr = ptr;
  595. while(*++argptr) ; // skip type string
  596. while(!*++argptr) ; // skip 0s
  597. char c;
  598. while((c = *++ptr))
  599. {
  600. switch(c)
  601. {
  602. case 'i':
  603. _dump_prim_arg<int32_t>(argptr, os); break;
  604. case 'c':
  605. _dump_prim_arg<int32_t, char>(argptr, os); break;
  606. // case 's':
  607. // _dump_prim_arg<char, const char*>(argptr, os); break;
  608. default:
  609. exit(1);
  610. }
  611. }
  612. }
  613. #endif
  614. int msg_id=0;
  615. /*
  616. * Master audio out (the final sound)
  617. */
  618. bool Master::AudioOut(float *outr, float *outl)
  619. {
  620. //Danger Limits
  621. if(memory->lowMemory(2,1024*1024))
  622. printf("QUITE LOW MEMORY IN THE RT POOL BE PREPARED FOR WEIRD BEHAVIOR!!\n");
  623. //Normal Limits
  624. if(!pendingMemory && memory->lowMemory(4,1024*1024)) {
  625. printf("Requesting more memory\n");
  626. bToU->write("/request-memory", "");
  627. pendingMemory = true;
  628. }
  629. //Handle watch points
  630. if(bToU)
  631. watcher.write_back = bToU;
  632. watcher.tick();
  633. //Handle user events TODO move me to a proper location
  634. char loc_buf[1024];
  635. DataObj d{loc_buf, 1024, this, bToU};
  636. memset(loc_buf, 0, sizeof(loc_buf));
  637. int events = 0;
  638. while(uToB && uToB->hasNext() && events < 100) {
  639. const char *msg = uToB->read();
  640. if(!strcmp(msg, "/load-master")) {
  641. Master *this_master = this;
  642. Master *new_master = *(Master**)rtosc_argument(msg, 0).b.data;
  643. new_master->AudioOut(outl, outr);
  644. Nio::masterSwap(new_master);
  645. if (mastercb)
  646. mastercb(mastercb_ptr, new_master);
  647. bToU->write("/free", "sb", "Master", sizeof(Master*), &this_master);
  648. return false;
  649. }
  650. //XXX yes, this is not realtime safe, but it is useful...
  651. if(strcmp(msg, "/get-vu") && false) {
  652. fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 5 + 30, 0 + 40);
  653. fprintf(stdout, "backend[%d]: '%s'<%s>\n", msg_id++, msg,
  654. rtosc_argument_string(msg));
  655. fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  656. }
  657. ports.dispatch(msg, d, true);
  658. events++;
  659. if(!d.matches) {// && !ports.apropos(msg)) {
  660. fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 1, 7 + 30, 0 + 40);
  661. fprintf(stderr, "Unknown address<BACKEND> '%s:%s'\n", uToB->peak(), rtosc_argument_string(uToB->peak()));
  662. #if 0
  663. if(strstr(msg, "PFMVelocity"))
  664. dump_msg(msg);
  665. if(ports.apropos(msg))
  666. fprintf(stderr, " -> best match: '%s'\n", ports.apropos(msg)->name);
  667. if(ports.apropos(msg+1))
  668. fprintf(stderr, " -> best match: '%s'\n", ports.apropos(msg+1)->name);
  669. #endif
  670. fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  671. }
  672. }
  673. if(events>1 && false)
  674. fprintf(stderr, "backend: %d events per cycle\n",events);
  675. //Swaps the Left channel with Right Channel
  676. if(swaplr)
  677. swap(outl, outr);
  678. //clean up the output samples (should not be needed?)
  679. memset(outl, 0, synth.bufferbytes);
  680. memset(outr, 0, synth.bufferbytes);
  681. //Compute part samples and store them part[npart]->partoutl,partoutr
  682. for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
  683. if(part[npart]->Penabled)
  684. part[npart]->ComputePartSmps();
  685. //Insertion effects
  686. for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
  687. if(Pinsparts[nefx] >= 0) {
  688. int efxpart = Pinsparts[nefx];
  689. if(part[efxpart]->Penabled)
  690. insefx[nefx]->out(part[efxpart]->partoutl,
  691. part[efxpart]->partoutr);
  692. }
  693. //Apply the part volumes and pannings (after insertion effects)
  694. for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
  695. if(!part[npart]->Penabled)
  696. continue;
  697. Stereo<float> newvol(part[npart]->volume),
  698. oldvol(part[npart]->oldvolumel,
  699. part[npart]->oldvolumer);
  700. float pan = part[npart]->panning;
  701. if(pan < 0.5f)
  702. newvol.l *= pan * 2.0f;
  703. else
  704. newvol.r *= (1.0f - pan) * 2.0f;
  705. //if(npart==0)
  706. //printf("[%d]vol = %f->%f\n", npart, oldvol.l, newvol.l);
  707. //the volume or the panning has changed and needs interpolation
  708. if(ABOVE_AMPLITUDE_THRESHOLD(oldvol.l, newvol.l)
  709. || ABOVE_AMPLITUDE_THRESHOLD(oldvol.r, newvol.r)) {
  710. for(int i = 0; i < synth.buffersize; ++i) {
  711. Stereo<float> vol(INTERPOLATE_AMPLITUDE(oldvol.l, newvol.l,
  712. i, synth.buffersize),
  713. INTERPOLATE_AMPLITUDE(oldvol.r, newvol.r,
  714. i, synth.buffersize));
  715. part[npart]->partoutl[i] *= vol.l;
  716. part[npart]->partoutr[i] *= vol.r;
  717. }
  718. part[npart]->oldvolumel = newvol.l;
  719. part[npart]->oldvolumer = newvol.r;
  720. }
  721. else {
  722. for(int i = 0; i < synth.buffersize; ++i) { //the volume did not changed
  723. part[npart]->partoutl[i] *= newvol.l;
  724. part[npart]->partoutr[i] *= newvol.r;
  725. }
  726. }
  727. }
  728. //System effects
  729. for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) {
  730. if(sysefx[nefx]->geteffect() == 0)
  731. continue; //the effect is disabled
  732. float tmpmixl[synth.buffersize];
  733. float tmpmixr[synth.buffersize];
  734. //Clean up the samples used by the system effects
  735. memset(tmpmixl, 0, synth.bufferbytes);
  736. memset(tmpmixr, 0, synth.bufferbytes);
  737. //Mix the channels according to the part settings about System Effect
  738. for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
  739. //skip if the part has no output to effect
  740. if(Psysefxvol[nefx][npart] == 0)
  741. continue;
  742. //skip if the part is disabled
  743. if(part[npart]->Penabled == 0)
  744. continue;
  745. //the output volume of each part to system effect
  746. const float vol = sysefxvol[nefx][npart];
  747. for(int i = 0; i < synth.buffersize; ++i) {
  748. tmpmixl[i] += part[npart]->partoutl[i] * vol;
  749. tmpmixr[i] += part[npart]->partoutr[i] * vol;
  750. }
  751. }
  752. // system effect send to next ones
  753. for(int nefxfrom = 0; nefxfrom < nefx; ++nefxfrom)
  754. if(Psysefxsend[nefxfrom][nefx] != 0) {
  755. const float vol = sysefxsend[nefxfrom][nefx];
  756. for(int i = 0; i < synth.buffersize; ++i) {
  757. tmpmixl[i] += sysefx[nefxfrom]->efxoutl[i] * vol;
  758. tmpmixr[i] += sysefx[nefxfrom]->efxoutr[i] * vol;
  759. }
  760. }
  761. sysefx[nefx]->out(tmpmixl, tmpmixr);
  762. //Add the System Effect to sound output
  763. const float outvol = sysefx[nefx]->sysefxgetvolume();
  764. for(int i = 0; i < synth.buffersize; ++i) {
  765. outl[i] += tmpmixl[i] * outvol;
  766. outr[i] += tmpmixr[i] * outvol;
  767. }
  768. }
  769. //Mix all parts
  770. for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
  771. if(part[npart]->Penabled) //only mix active parts
  772. for(int i = 0; i < synth.buffersize; ++i) { //the volume did not changed
  773. outl[i] += part[npart]->partoutl[i];
  774. outr[i] += part[npart]->partoutr[i];
  775. }
  776. //Insertion effects for Master Out
  777. for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
  778. if(Pinsparts[nefx] == -2)
  779. insefx[nefx]->out(outl, outr);
  780. //Master Volume
  781. for(int i = 0; i < synth.buffersize; ++i) {
  782. outl[i] *= volume;
  783. outr[i] *= volume;
  784. }
  785. vuUpdate(outl, outr);
  786. //Shutup if it is asked (with fade-out)
  787. if(shutup) {
  788. for(int i = 0; i < synth.buffersize; ++i) {
  789. float tmp = (synth.buffersize_f - i) / synth.buffersize_f;
  790. outl[i] *= tmp;
  791. outr[i] *= tmp;
  792. }
  793. ShutUp();
  794. }
  795. //update the global frame timer
  796. time++;
  797. return true;
  798. }
  799. //TODO review the respective code from yoshimi for this
  800. //If memory serves correctly, libsamplerate was used
  801. void Master::GetAudioOutSamples(size_t nsamples,
  802. unsigned samplerate,
  803. float *outl,
  804. float *outr)
  805. {
  806. off_t out_off = 0;
  807. //Fail when resampling rather than doing a poor job
  808. if(synth.samplerate != samplerate) {
  809. printf("darn it: %d vs %d\n", synth.samplerate, samplerate);
  810. return;
  811. }
  812. while(nsamples) {
  813. //use all available samples
  814. if(nsamples >= smps) {
  815. memcpy(outl + out_off, bufl + off, sizeof(float) * smps);
  816. memcpy(outr + out_off, bufr + off, sizeof(float) * smps);
  817. nsamples -= smps;
  818. //generate samples
  819. if (! AudioOut(bufl, bufr))
  820. return;
  821. off = 0;
  822. out_off += smps;
  823. smps = synth.buffersize;
  824. }
  825. else { //use some samples
  826. memcpy(outl + out_off, bufl + off, sizeof(float) * nsamples);
  827. memcpy(outr + out_off, bufr + off, sizeof(float) * nsamples);
  828. smps -= nsamples;
  829. off += nsamples;
  830. nsamples = 0;
  831. }
  832. }
  833. }
  834. Master::~Master()
  835. {
  836. delete []bufl;
  837. delete []bufr;
  838. for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
  839. delete part[npart];
  840. for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
  841. delete insefx[nefx];
  842. for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx)
  843. delete sysefx[nefx];
  844. delete fft;
  845. delete memory;
  846. }
  847. /*
  848. * Parameter control
  849. */
  850. void Master::setPvolume(char Pvolume_)
  851. {
  852. Pvolume = Pvolume_;
  853. volume = dB2rap((Pvolume - 96.0f) / 96.0f * 40.0f);
  854. }
  855. void Master::setPkeyshift(char Pkeyshift_)
  856. {
  857. Pkeyshift = Pkeyshift_;
  858. keyshift = (int)Pkeyshift - 64;
  859. }
  860. void Master::setPsysefxvol(int Ppart, int Pefx, char Pvol)
  861. {
  862. Psysefxvol[Pefx][Ppart] = Pvol;
  863. sysefxvol[Pefx][Ppart] = powf(0.1f, (1.0f - Pvol / 96.0f) * 2.0f);
  864. }
  865. void Master::setPsysefxsend(int Pefxfrom, int Pefxto, char Pvol)
  866. {
  867. Psysefxsend[Pefxfrom][Pefxto] = Pvol;
  868. sysefxsend[Pefxfrom][Pefxto] = powf(0.1f, (1.0f - Pvol / 96.0f) * 2.0f);
  869. }
  870. /*
  871. * Panic! (Clean up all parts and effects)
  872. */
  873. void Master::ShutUp()
  874. {
  875. for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
  876. part[npart]->cleanup();
  877. fakepeakpart[npart] = 0;
  878. }
  879. for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
  880. insefx[nefx]->cleanup();
  881. for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx)
  882. sysefx[nefx]->cleanup();
  883. vuresetpeaks();
  884. shutup = 0;
  885. }
  886. /*
  887. * Reset peaks and clear the "cliped" flag (for VU-meter)
  888. */
  889. void Master::vuresetpeaks()
  890. {
  891. vu.outpeakl = 1e-9;
  892. vu.outpeakr = 1e-9;
  893. vu.maxoutpeakl = 1e-9;
  894. vu.maxoutpeakr = 1e-9;
  895. vu.clipped = 0;
  896. }
  897. void Master::applyparameters(void)
  898. {
  899. for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
  900. part[npart]->applyparameters();
  901. }
  902. void Master::initialize_rt(void)
  903. {
  904. for(int i=0; i<NUM_SYS_EFX; ++i)
  905. sysefx[i]->init();
  906. for(int i=0; i<NUM_INS_EFX; ++i)
  907. insefx[i]->init();
  908. for(int i=0; i<NUM_MIDI_PARTS; ++i)
  909. part[i]->initialize_rt();
  910. }
  911. void Master::add2XML(XMLwrapper& xml)
  912. {
  913. xml.addpar("volume", Pvolume);
  914. xml.addpar("key_shift", Pkeyshift);
  915. xml.addparbool("nrpn_receive", ctl.NRPN.receive);
  916. xml.beginbranch("MICROTONAL");
  917. microtonal.add2XML(xml);
  918. xml.endbranch();
  919. for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
  920. xml.beginbranch("PART", npart);
  921. part[npart]->add2XML(xml);
  922. xml.endbranch();
  923. }
  924. xml.beginbranch("SYSTEM_EFFECTS");
  925. for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) {
  926. xml.beginbranch("SYSTEM_EFFECT", nefx);
  927. xml.beginbranch("EFFECT");
  928. sysefx[nefx]->add2XML(xml);
  929. xml.endbranch();
  930. for(int pefx = 0; pefx < NUM_MIDI_PARTS; ++pefx) {
  931. xml.beginbranch("VOLUME", pefx);
  932. xml.addpar("vol", Psysefxvol[nefx][pefx]);
  933. xml.endbranch();
  934. }
  935. for(int tonefx = nefx + 1; tonefx < NUM_SYS_EFX; ++tonefx) {
  936. xml.beginbranch("SENDTO", tonefx);
  937. xml.addpar("send_vol", Psysefxsend[nefx][tonefx]);
  938. xml.endbranch();
  939. }
  940. xml.endbranch();
  941. }
  942. xml.endbranch();
  943. xml.beginbranch("INSERTION_EFFECTS");
  944. for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) {
  945. xml.beginbranch("INSERTION_EFFECT", nefx);
  946. xml.addpar("part", Pinsparts[nefx]);
  947. xml.beginbranch("EFFECT");
  948. insefx[nefx]->add2XML(xml);
  949. xml.endbranch();
  950. xml.endbranch();
  951. }
  952. xml.endbranch();
  953. }
  954. int Master::getalldata(char **data)
  955. {
  956. XMLwrapper xml;
  957. xml.beginbranch("MASTER");
  958. add2XML(xml);
  959. xml.endbranch();
  960. *data = xml.getXMLdata();
  961. return strlen(*data) + 1;
  962. }
  963. void Master::putalldata(const char *data)
  964. {
  965. XMLwrapper xml;
  966. if(!xml.putXMLdata(data)) {
  967. return;
  968. }
  969. if(xml.enterbranch("MASTER") == 0)
  970. return;
  971. getfromXML(xml);
  972. xml.exitbranch();
  973. }
  974. int Master::saveXML(const char *filename)
  975. {
  976. XMLwrapper xml;
  977. xml.beginbranch("MASTER");
  978. add2XML(xml);
  979. xml.endbranch();
  980. return xml.saveXMLfile(filename, gzip_compression);
  981. }
  982. int Master::loadXML(const char *filename)
  983. {
  984. XMLwrapper xml;
  985. if(xml.loadXMLfile(filename) < 0) {
  986. return -1;
  987. }
  988. if(xml.enterbranch("MASTER") == 0)
  989. return -10;
  990. getfromXML(xml);
  991. xml.exitbranch();
  992. initialize_rt();
  993. return 0;
  994. }
  995. void Master::getfromXML(XMLwrapper& xml)
  996. {
  997. setPvolume(xml.getpar127("volume", Pvolume));
  998. setPkeyshift(xml.getpar127("key_shift", Pkeyshift));
  999. ctl.NRPN.receive = xml.getparbool("nrpn_receive", ctl.NRPN.receive);
  1000. part[0]->Penabled = 0;
  1001. for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
  1002. if(xml.enterbranch("PART", npart) == 0)
  1003. continue;
  1004. part[npart]->getfromXML(xml);
  1005. xml.exitbranch();
  1006. }
  1007. if(xml.enterbranch("MICROTONAL")) {
  1008. microtonal.getfromXML(xml);
  1009. xml.exitbranch();
  1010. }
  1011. sysefx[0]->changeeffect(0);
  1012. if(xml.enterbranch("SYSTEM_EFFECTS")) {
  1013. for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) {
  1014. if(xml.enterbranch("SYSTEM_EFFECT", nefx) == 0)
  1015. continue;
  1016. if(xml.enterbranch("EFFECT")) {
  1017. sysefx[nefx]->getfromXML(xml);
  1018. xml.exitbranch();
  1019. }
  1020. for(int partefx = 0; partefx < NUM_MIDI_PARTS; ++partefx) {
  1021. if(xml.enterbranch("VOLUME", partefx) == 0)
  1022. continue;
  1023. setPsysefxvol(partefx, nefx,
  1024. xml.getpar127("vol", Psysefxvol[partefx][nefx]));
  1025. xml.exitbranch();
  1026. }
  1027. for(int tonefx = nefx + 1; tonefx < NUM_SYS_EFX; ++tonefx) {
  1028. if(xml.enterbranch("SENDTO", tonefx) == 0)
  1029. continue;
  1030. setPsysefxsend(nefx, tonefx,
  1031. xml.getpar127("send_vol",
  1032. Psysefxsend[nefx][tonefx]));
  1033. xml.exitbranch();
  1034. }
  1035. xml.exitbranch();
  1036. }
  1037. xml.exitbranch();
  1038. }
  1039. if(xml.enterbranch("INSERTION_EFFECTS")) {
  1040. for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) {
  1041. if(xml.enterbranch("INSERTION_EFFECT", nefx) == 0)
  1042. continue;
  1043. Pinsparts[nefx] = xml.getpar("part",
  1044. Pinsparts[nefx],
  1045. -2,
  1046. NUM_MIDI_PARTS);
  1047. if(xml.enterbranch("EFFECT")) {
  1048. insefx[nefx]->getfromXML(xml);
  1049. xml.exitbranch();
  1050. }
  1051. xml.exitbranch();
  1052. }
  1053. xml.exitbranch();
  1054. }
  1055. }