Change ALSA device default to 'default' from hw:0
[zynaddsubfx-code.git] / src / Misc / Master.cpp
blob63dd44e2a97ff8268a12dbb37582f34197c9393a
1 /*
2 ZynAddSubFX - a software synthesizer
4 Master.cpp - It sends Midi Messages to Parts, receives samples from parts,
5 process them with system/insertion effects and mix them
6 Copyright (C) 2002-2005 Nasca Octavian Paul
7 Author: Nasca Octavian Paul
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
15 #include "Master.h"
17 #include "Part.h"
19 #include "zyn-version.h"
20 #include "../Misc/Stereo.h"
21 #include "../Misc/Util.h"
22 #include "../Params/LFOParams.h"
23 #include "../Effects/EffectMgr.h"
24 #include "../DSP/FFTwrapper.h"
25 #include "../Misc/Allocator.h"
26 #include "../Containers/ScratchString.h"
27 #include "../Nio/Nio.h"
28 #include "PresetExtractor.h"
30 #include <rtosc/ports.h>
31 #include <rtosc/port-sugar.h>
32 #include <rtosc/thread-link.h>
33 #include <stdio.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <fstream>
37 #include <iostream>
38 #include <algorithm>
39 #include <cmath>
40 #include <atomic>
41 #include <unistd.h>
43 using namespace std;
44 using namespace rtosc;
46 namespace zyn {
48 #define rObject Master
50 static const Ports sysefxPort =
52 {"part#" STRINGIFY(NUM_MIDI_PARTS) "::i", rProp(parameter) rDefault([0...])
53 rDoc("gain on part to sysefx routing"), 0,
54 [](const char *m, RtData&d)
56 //we know that if we are here the location must
57 //be ...Psysefxvol#N/part#M
58 //and the number "N" is one or two digits at most
60 // go backto the '/'
61 const char* m_findslash = m + strlen(m),
62 * loc_findslash = d.loc + strlen(d.loc);
63 for(;*loc_findslash != '/'; --m_findslash, --loc_findslash)
64 assert(*loc_findslash == *m_findslash);
65 assert(m_findslash + 1 == m);
67 const char *index_1 = loc_findslash-1;
68 assert(isdigit(*index_1));
69 if(isdigit(index_1[-1]))
70 index_1--;
71 int ind1 = atoi(index_1); //efx
73 //Now get the second index like normal
74 while(!isdigit(*m)) m++;
75 int ind2 = atoi(m); //part
76 Master &mast = *(Master*)d.obj;
78 if(rtosc_narguments(m)) {
79 mast.setPsysefxvol(ind2, ind1, rtosc_argument(m,0).i /*vol*/);
80 d.broadcast(d.loc, "i", mast.Psysefxvol[ind1][ind2]);
81 } else
82 d.reply(d.loc, "i", mast.Psysefxvol[ind1][ind2]);
86 static const Ports sysefsendto =
88 {"to#" STRINGIFY(NUM_SYS_EFX) "::i", rProp(parameter) rDefault([0...])
89 rDoc("sysefx to sysefx routing gain"), 0, [](const char *m, RtData&d)
91 //same workaround as before
92 //go backto the '/'
93 const char* m_findslash = m + strlen(m),
94 * loc_findslash = d.loc + strlen(d.loc);
95 for(;*loc_findslash != '/'; --m_findslash, --loc_findslash)
96 assert(*loc_findslash == *m_findslash);
97 assert(m_findslash + 1 == m);
99 const char *index_1 = loc_findslash-1;
100 assert(isdigit(*index_1));
101 if(isdigit(index_1[-1]))
102 index_1--;
103 int ind1 = atoi(index_1);
105 //Now get the second index like normal
106 while(!isdigit(*m)) m++;
107 int ind2 = atoi(m);
108 Master &master = *(Master*)d.obj;
110 if(rtosc_narguments(m))
112 master.setPsysefxsend(ind1, ind2, rtosc_argument(m,0).i);
113 d.broadcast(d.loc, "i", master.Psysefxsend[ind1][ind2]);
115 else
116 d.reply(d.loc, "i", master.Psysefxsend[ind1][ind2]);
120 #define rBegin [](const char *msg, RtData &d) { \
121 (void) msg; \
122 rtosc::AutomationMgr &a = *(AutomationMgr*)d.obj
123 #define rEnd }
125 static int extract_num(const char *&msg)
127 while(*msg && !isdigit(*msg)) msg++;
128 int num = atoi(msg);
129 while(isdigit(*msg)) msg++;
130 return num;
133 static int get_next_int(const char *msg)
135 return extract_num(msg);
138 static const Ports mapping_ports = {
139 {"offset::f", rProp(parameter) rDefault(0) rShort("off") rLinear(-50, 50) rMap(unit, percent), 0,
140 rBegin;
141 int slot = d.idx[1];
142 int param = d.idx[0];
143 if(!strcmp("f",rtosc_argument_string(msg))) {
144 a.setSlotSubOffset(slot, param, rtosc_argument(msg, 0).f);
145 a.updateMapping(slot, param);
146 d.broadcast(d.loc, "f", a.getSlotSubOffset(slot, param));
147 } else
148 d.reply(d.loc, "f", a.getSlotSubOffset(slot, param));
149 rEnd},
150 {"gain::f", rProp(parameter) rDefault(100) rShort("gain") rLinear(-200, 200) rMap(unit, percent), 0,
151 rBegin;
152 int slot = d.idx[1];
153 int param = d.idx[0];
154 if(!strcmp("f",rtosc_argument_string(msg))) {
155 a.setSlotSubGain(slot, param, rtosc_argument(msg, 0).f);
156 a.updateMapping(slot, param);
157 d.broadcast(d.loc, "f", a.getSlotSubGain(slot, param));
158 } else
159 d.reply(d.loc, "f", a.getSlotSubGain(slot, param));
160 rEnd},
163 static const Ports auto_param_ports = {
164 {"used::T:F", rProp(parameter) rProp(read-only) rDoc("If automation is assigned to anything"), 0,
165 rBegin;
166 int slot = d.idx[1];
167 int param = d.idx[0];
169 d.reply(d.loc, a.slots[slot].automations[param].used ? "T" : "F");
170 rEnd},
171 {"active::T:F", rProp(parameter) rDoc("If automation is being actively used"), 0,
172 rBegin;
173 int slot = d.idx[1];
174 int param = d.idx[0];
175 if(rtosc_narguments(msg)) {
176 a.slots[slot].automations[param].active = rtosc_argument(msg, 0).T;
177 d.broadcast(d.loc, a.slots[slot].automations[param].active ? "T" : "F");
179 else
180 d.reply(d.loc, a.slots[slot].automations[param].active ? "T" : "F");
181 rEnd},
182 {"path::s", rProp(parameter) rDoc("Path of parameter"), 0,
183 rBegin;
184 int slot = d.idx[1];
185 int param = d.idx[0];
186 if(!strcmp("s",rtosc_argument_string(msg))) {
187 a.setSlotSubPath(slot, param, rtosc_argument(msg, 0).s);
188 a.updateMapping(slot, param);
189 d.broadcast(d.loc, "s", a.slots[slot].automations[param].param_path);
191 else
192 d.reply(d.loc, "s", a.slots[slot].automations[param].param_path);
193 rEnd},
194 {"clear:", rDoc("Clear automation param"), 0,
195 rBegin;
196 int slot = d.idx[1];
197 int param = d.idx[0];
198 a.clearSlotSub(slot, param);
199 rEnd},
200 {"mapping/", 0, &mapping_ports,
201 rBegin;
202 (void) a;
203 SNIP;
204 mapping_ports.dispatch(msg, d);
205 rEnd},
207 //{"mapping", rDoc("Parameter mapping control"), 0,
208 // rBegin;
209 // int slot = d.idx[1];
210 // int param = d.idx[0];
211 // if(!strcmp("b", rtosc_argument_string(msg))) {
212 // int len = rtosc_argument(msg, 0).b.len / sizeof(float);
213 // float *data = (float*)rtosc_argument(msg, 0).b.data;
214 // } else {
215 // d.reply(d.loc, "b",
216 // a.slots[slot].automations[param].map.npoints*sizeof(float),
217 // a.slots[slot].automations[param].map.control_points);
218 // }
219 // rEnd},
222 static const Ports slot_ports = {
223 //{"learn-binding:s", rDoc("Create binding for automation path with midi-learn"), 0,
224 // rBegin;
225 // (void) m;
226 // //m->automate.createBinding(rtosc_argument(msg, 0).i,
227 // // rtosc_argument(msg, 1).s,
228 // // rtosc_argument(msg, 2).T);
229 // rEnd},
230 //{"create-binding:s", rDoc("Create binding for automation path"), 0,
231 // rBegin;
232 // m->automate.createBinding(rtosc_argument(msg, 0).i,
233 // rtosc_argument(msg, 1).s,
234 // rtosc_argument(msg, 2).T);
235 // rEnd},
236 {"value::f", rProp(no learn) rProp(parameter) rMap(default, 0.5) rLinear(0, 1) rDoc("Access current value in slot 'i' (0..1)"), 0,
237 rBegin;
238 int num = d.idx[0];
239 if(!strcmp("f",rtosc_argument_string(msg))) {
240 a.setSlot(num, rtosc_argument(msg, 0).f);
241 d.broadcast(d.loc, "f", a.getSlot(num));
242 } else
243 d.reply(d.loc, "f", a.getSlot(num));
244 rEnd},
246 {"name::s", rProp(parameter) rDoc("Access name of automation slot"), 0,
247 rBegin;
248 int num = d.idx[0];
249 if(!strcmp("s",rtosc_argument_string(msg))) {
250 a.setName(num, rtosc_argument(msg, 0).s);
251 d.broadcast(d.loc, "s", a.getName(num));
252 } else
253 d.reply(d.loc, "s", a.getName(num));
254 rEnd},
255 {"midi-cc::i", rProp(parameter) rMap(default, -1) rDoc("Access assigned midi CC slot") , 0,
256 rBegin;
257 int slot = d.idx[0];
258 if(rtosc_narguments(msg)) {
259 a.slots[slot].midi_cc = rtosc_argument(msg, 0).i;
260 d.broadcast(d.loc, "i", a.slots[slot].midi_cc);
261 } else
262 d.reply(d.loc, "i", a.slots[slot].midi_cc);
264 rEnd},
265 {"midi-nrpn::i", rProp(parameter) rMap(default, -1) rDoc("Access assigned midi NRPN slot") , 0,
266 rBegin;
267 int slot = d.idx[0];
268 if(rtosc_narguments(msg)) {
269 a.slots[slot].midi_nrpn = rtosc_argument(msg, 0).i;
270 d.broadcast(d.loc, "i", a.slots[slot].midi_nrpn);
271 } else
272 d.reply(d.loc, "i", a.slots[slot].midi_nrpn);
274 rEnd},
275 {"active::T:F", rProp(parameter) rMap(default, F) rDoc("If Slot is enabled"), 0,
276 rBegin;
277 int slot = d.idx[0];
278 if(rtosc_narguments(msg)) {
279 a.slots[slot].active = rtosc_argument(msg, 0).T;
280 d.broadcast(d.loc, a.slots[slot].active ? "T" : "F");
282 else
283 d.reply(d.loc, a.slots[slot].active ? "T" : "F");
284 rEnd},
285 {"learning::i", rProp(parameter) rMap(default, -1) rDoc("If slot is trying to find a midi learn binding"), 0,
286 rBegin;
287 int slot = d.idx[0];
288 d.reply(d.loc, "i", a.slots[slot].learning);
289 rEnd},
290 {"clear:", rDoc("Clear automation slot"), 0,
291 rBegin;
292 int slot = d.idx[0];
293 a.clearSlot(slot);
294 rEnd},
295 {"param#4/", rDoc("Info on individual param mappings"), &auto_param_ports,
296 rBegin;
297 (void)a;
298 d.push_index(get_next_int(msg));
299 SNIP;
300 auto_param_ports.dispatch(msg, d);
301 d.pop_index();
302 rEnd},
305 static const Ports automate_ports = {
306 {"active-slot::i", rProp(parameter) rMap(min, -1) rMap(max, 16) rDoc("Active Slot for macro learning"), 0,
307 rBegin;
308 if(!strcmp("i",rtosc_argument_string(msg))) {
309 a.active_slot = rtosc_argument(msg, 0).i;
310 d.broadcast(d.loc, "i", a.active_slot);
311 } else
312 d.reply(d.loc, "i", a.active_slot);
313 rEnd},
314 {"learn-binding-new-slot:s", rDoc("Learn a parameter assigned to a new slot"), 0,
315 rBegin;
316 int free_slot = a.free_slot();
317 if(free_slot >= 0) {
318 a.createBinding(free_slot, rtosc_argument(msg, 0).s, true);
319 a.active_slot = free_slot;
321 rEnd},
322 {"learn-binding-same-slot:s", rDoc("Learn a parameter appending to the active-slot"), 0,
323 rBegin;
324 if(a.active_slot >= 0)
325 a.createBinding(a.active_slot, rtosc_argument(msg, 0).s, true);
326 rEnd},
327 {"slot#16/", rDoc("Parameters of individual automation slots"), &slot_ports,
328 rBegin;
329 (void)a;
330 d.push_index(get_next_int(msg));
331 SNIP;
332 slot_ports.dispatch(msg, d);
333 d.pop_index();
334 rEnd},
335 {"clear", rDoc("Clear all automation slots"), 0,
336 rBegin;
337 for(int i=0; i<a.nslots; ++i)
338 a.clearSlot(i);
339 rEnd},
340 {"load-blob:b", rProp(internal) rDoc("Load blob from middleware"), 0,
341 rBegin;
342 auto &b = **(rtosc::AutomationMgr **)rtosc_argument(msg, 0).b.data;
343 //XXX this code should likely be in rtosc
344 for(int i=0; i<a.nslots; ++i) {
345 auto &slota = a.slots[i];
346 auto &slotb = b.slots[i];
347 std::swap(slota.learning, slotb.learning);
348 std::swap(slota.midi_cc, slotb.midi_cc);
349 std::swap(slota.used, slotb.used);
350 std::swap(slota.active, slotb.active);
351 for(int j=0; j<a.per_slot; ++j) {
352 auto &aa = slota.automations[j];
353 auto &ab = slotb.automations[j];
354 std::swap(aa.used, ab.used);
355 std::swap(aa.active, ab.active);
356 std::swap(aa.param_path, ab.param_path);
357 std::swap(aa.param_min, ab.param_min);
358 std::swap(aa.param_max, ab.param_max);
359 std::swap(aa.param_step, ab.param_step);
360 std::swap(aa.param_type, ab.param_type);
361 std::swap(aa.map.offset, ab.map.offset);
362 std::swap(aa.map.gain, ab.map.gain);
363 std::swap(aa.map.upoints, ab.map.upoints);
364 for(int k=0; k<aa.map.npoints; ++k)
365 std::swap(aa.map.control_points[k], ab.map.control_points[k]);
369 rtosc::AutomationMgr* ptr = &b;
370 d.reply("/free", "sb", "rtosc::AutomationMgr", sizeof(rtosc::AutomationMgr*), &ptr);
372 rEnd},
375 #undef rBegin
376 #undef rEnd
377 #define rBegin [](const char *msg, RtData &d) { (void)msg; Master *m = (Master*)d.obj
378 #define rEnd }
380 static const Ports watchPorts = {
381 {"add:s", rDoc("Add synthesis state to watch"), 0,
382 rBegin;
383 if(!m->watcher.active(rtosc_argument(msg,0).s))
384 m->watcher.add_watch(rtosc_argument(msg,0).s);
385 rEnd},
389 extern const Ports bankPorts;
390 static const Ports master_ports = {
391 rString(last_xmz, XMZ_PATH_MAX, "File name for last name loaded if any."),
392 rRecursp(part, 16, "Part"),//NUM_MIDI_PARTS
393 rRecursp(sysefx, 4, "System Effect"),//NUM_SYS_EFX
394 rRecursp(insefx, 8, "Insertion Effect"),//NUM_INS_EFX
395 rRecur(HDDRecorder, "HDD recorder"),
396 rRecur(microtonal, "Microtonal Mapping Functionality"),
397 rRecur(ctl, "Controller"),
398 rArrayOption(Pinsparts, NUM_INS_EFX, rOpt(-2, Master), rOpt(-1, Off),
399 rOptions(Part1, Part2, Part3, Part4, Part5, Part6,
400 Part7, Part8, Part9, Part10, Part11, Part12,
401 Part13, Part14, Part15, Part16) rDefault([Off ...]),
402 "Part to insert part onto"),
403 {"Pkeyshift::i", rShort("key shift") rProp(parameter) rLinear(0,127)
404 rDefault(64) rDoc("Global Key Shift"), 0, [](const char *m, RtData&d) {
405 if(rtosc_narguments(m)==0) {
406 d.reply(d.loc, "i", ((Master*)d.obj)->Pkeyshift);
407 } else if(rtosc_narguments(m)==1 && rtosc_type(m,0)=='i') {
408 ((Master*)d.obj)->setPkeyshift(limit<char>(rtosc_argument(m,0).i,0,127));
409 d.broadcast(d.loc, "i", ((Master*)d.obj)->Pkeyshift);}}},
410 {"echo", rDoc("Hidden port to echo messages"), 0, [](const char *m, RtData&d) {
411 d.reply(m-1);}},
412 {"get-vu:", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) {
413 Master *m = (Master*)d.obj;
414 d.reply("/vu-meter", "bb", sizeof(m->vu), &m->vu, sizeof(float)*NUM_MIDI_PARTS, m->vuoutpeakpartl);}},
415 {"vu-meter:", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) {
416 Master *m = (Master*)d.obj;
417 char types[6+2*NUM_MIDI_PARTS+1] = {};
418 rtosc_arg_t args[6+2*NUM_MIDI_PARTS+1];
419 for(int i=0; i<6+2*NUM_MIDI_PARTS; ++i)
420 types[i] = 'f';
421 args[0].f = m->vu.outpeakl;
422 args[1].f = m->vu.outpeakr;
423 args[2].f = m->vu.maxoutpeakl;
424 args[3].f = m->vu.maxoutpeakr;
425 args[4].f = m->vu.rmspeakl;
426 args[5].f = m->vu.rmspeakr;
427 for(int i=0; i<NUM_MIDI_PARTS; ++i) {
428 args[6 + 2 * i].f = m->vuoutpeakpartl[i];
429 args[6 + 2 * i + 1].f = m->vuoutpeakpartr[i];
431 d.replyArray("/vu-meter", types, args);}},
432 {"reset-vu:", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) {
433 Master *m = (Master*)d.obj;
434 m->vuresetpeaks();}},
435 {"load-part:ib", rProp(internal) rDoc("Load Part From Middleware"), 0, [](const char *msg, RtData &d) {
436 Master *m = (Master*)d.obj;
437 Part *p = *(Part**)rtosc_argument(msg, 1).b.data;
438 int i = rtosc_argument(msg, 0).i;
439 m->part[i]->cloneTraits(*p);
440 m->part[i]->kill_rt();
441 d.reply("/free", "sb", "Part", sizeof(void*), &m->part[i]);
442 m->part[i] = p;
443 p->initialize_rt();
444 memset(m->activeNotes, 0, sizeof(m->activeNotes));
446 {"active_keys:", rProp("Obtain a list of active notes"), 0,
447 rBegin;
448 char keys[129] = {};
449 for(int i=0; i<128; ++i)
450 keys[i] = m->activeNotes[i] ? 'T' : 'F';
451 d.broadcast(d.loc, keys);
452 rEnd},
453 {"Pvolume::i", rShort("volume") rProp(parameter) rLinear(0,127)
454 rDefault(80) rDoc("Master Volume"), 0,
455 [](const char *m, rtosc::RtData &d) {
456 if(rtosc_narguments(m)==0) {
457 d.reply(d.loc, "i", (int) roundf(96.0f * ((Master*)d.obj)->Volume / 40.0f + 96.0f));
458 } else if(rtosc_narguments(m)==1 && rtosc_type(m,0)=='i') {
459 ((Master *)d.obj)->Volume = ((Master *)d.obj)->volume127ToFloat(limit<unsigned char>(rtosc_argument(m, 0).i, 0, 127));
460 d.broadcast(d.loc, "i", limit<char>(rtosc_argument(m, 0).i, 0, 127));
461 }}},
462 {"volume::i", rShort("volume") rProp(parameter) rLinear(0,127)
463 rDoc("Master Volume"), 0,
464 [](const char *m, rtosc::RtData &d) {
465 Master *master = (Master *)d.obj;
466 if(rtosc_narguments(m)==0) {
467 d.reply(d.loc, "i", (int) roundf(96.0f * master->Volume / 40.0f + 96.0f));
468 } else if (rtosc_narguments(m)==1 && rtosc_type(m,0)=='i') {
469 master->Volume = master->volume127ToFloat(limit<unsigned char>(rtosc_argument(m, 0).i, 0, 127));
470 d.broadcast(d.loc, "i", limit<char>(rtosc_argument(m, 0).i, 0, 127));
471 }}},
472 rParamF(Volume, rShort("volume"), rDefault(-6.66667f), rLinear(-40.0f,13.3333f),
473 rUnit(dB), "Master Volume"),
474 {"Psysefxvol#" STRINGIFY(NUM_SYS_EFX) "/::i", 0, &sysefxPort,
475 [](const char *msg, rtosc::RtData &d) {
476 SNIP;
477 sysefxPort.dispatch(msg, d);
479 {"sysefxfrom#" STRINGIFY(NUM_SYS_EFX) "/", rDoc("Routing Between System Effects"), &sysefsendto,
480 [](const char *msg, RtData&d) {
481 SNIP;
482 sysefsendto.dispatch(msg, d);
484 {"noteOn:iii:iiif", rDoc("Noteon Event"), 0,
485 [](const char *m,RtData &d){
486 Master *M = (Master*)d.obj;
487 if (rtosc_narguments(m) > 3)
488 /* Manually specify the frequency as 4th argument */
489 M->noteOn(rtosc_argument(m,0).i,rtosc_argument(m,1).i,rtosc_argument(m,2).i,rtosc_argument(m,3).f);
490 else
491 /* Standard MIDI noteOn */
492 M->noteOn(rtosc_argument(m,0).i,rtosc_argument(m,1).i,rtosc_argument(m,2).i);
495 {"noteOff:ii", rDoc("Noteoff Event"), 0,
496 [](const char *m,RtData &d){
497 Master *M = (Master*)d.obj;
498 M->noteOff(rtosc_argument(m,0).i,rtosc_argument(m,1).i);}},
499 {"virtual_midi_cc:iii", rDoc("MIDI CC Event"), 0,
500 [](const char *m,RtData &d){
501 Master *M = (Master*)d.obj;
502 M->setController(rtosc_argument(m,0).i,rtosc_argument(m,1).i,rtosc_argument(m,2).i);}},
503 {"setController:iii", rDoc("MIDI CC Event"), 0,
504 [](const char *m,RtData &d){
505 Master *M = (Master*)d.obj;
506 M->setController(rtosc_argument(m,0).i,rtosc_argument(m,1).i,rtosc_argument(m,2).i);}},
507 {"tempo::i", rProp(parameter) rDefault(120) rShort("Tempo") rUnit(BPM) rDoc("Tempo / Beats per minute") rLinear(40, 200), 0,
508 rBegin;
509 if(!strcmp("i",rtosc_argument_string(msg))) {
510 m->time.tempo = rtosc_argument(msg, 0).i;
511 d.broadcast(d.loc, "i", m->time.tempo);
512 } else
513 d.reply(d.loc, "i", m->time.tempo);
514 rEnd},
515 {"Panic:", rDoc("Stop all sound"), 0,
516 [](const char *, RtData &d) {
517 Master &M = *(Master*)d.obj;
518 M.ShutUp();
520 {"freeze_state:", rProp(internal) rDoc("Disable OSC event handling\n"
521 "This sets up a read-only mode from which it's safe for another"
522 " thread to save parameters"), 0,
523 [](const char *,RtData &d) {
524 Master *M = (Master*)d.obj;
525 std::atomic_thread_fence(std::memory_order_release);
526 M->frozenState = true;
527 d.reply("/state_frozen", "");}},
528 {"thaw_state:", rProp(internal) rDoc("Resume handling OSC messages\n"
529 "See /freeze_state for more information"), 0,
530 [](const char *,RtData &d) {
531 Master *M = (Master*)d.obj;
532 M->frozenState = false;}},
533 {"midi-learn/", rDoc("MIDI Learn Classic"), &rtosc::MidiMapperRT::ports,
534 [](const char *msg, RtData &d) {
535 Master *M = (Master*)d.obj;
536 SNIP;
537 printf("residue message = <%s>\n", msg);
538 d.obj = &M->midi;
539 rtosc::MidiMapperRT::ports.dispatch(msg,d);}},
540 {"automate/", rDoc("MIDI Learn/Plugin Automation support"), &automate_ports,
541 [](const char *msg, RtData &d) {
542 SNIP;
543 d.obj = (void*)&((Master*)d.obj)->automate;
544 automate_ports.dispatch(msg, d);
546 {"close-ui:", rDoc("Request to close the unique connection named \"GUI\""), 0,
547 [](const char *, RtData &d) {
548 d.reply("/close-ui", "");}},
549 {"add-rt-memory:bi", rProp(internal) rDoc("Add Additional Memory To RT MemPool"), 0,
550 [](const char *msg, RtData &d)
552 Master &m = *(Master*)d.obj;
553 char *mem = *(char**)rtosc_argument(msg, 0).b.data;
554 int i = rtosc_argument(msg, 1).i;
555 m.memory->addMemory(mem, i);
556 m.pendingMemory = false;
558 {"samplerate:", rMap(unit, Hz) rDoc("Get synthesizer sample rate"), 0, [](const char *, RtData &d) {
559 Master &m = *(Master*)d.obj;
560 d.reply("/samplerate", "f", m.synth.samplerate_f);
562 {"oscilsize:", rDoc("Get synthesizer oscillator size"), 0, [](const char *, RtData &d) {
563 Master &m = *(Master*)d.obj;
564 d.reply("/oscilsize", "f", m.synth.oscilsize_f);
565 d.reply("/oscilsize", "i", m.synth.oscilsize);
567 {"undo_pause:",rProp(internal) rDoc("pause undo event recording"),0,
568 [](const char *, rtosc::RtData &d) {d.reply("/undo_pause", "");}},
569 {"undo_resume:",rProp(internal) rDoc("resume undo event recording"),0,
570 [](const char *, rtosc::RtData &d) {d.reply("/undo_resume", "");}},
571 {"last_dnd::s", rProp(internal) rDoc("Last Drag and Drop OSC path"),0,
572 rBOIL_BEGIN
573 if(!strcmp("", args)) {
574 data.reply(loc, "c", obj->dnd_buffer);
575 *obj->dnd_buffer = 0;
576 } else {
577 assert(!*obj->dnd_buffer);
578 const char* var = rtosc_argument(msg, 0).s;
579 printf("receiving /last_dnd %s\n",var);
580 strncpy(obj->dnd_buffer, var, Master::dnd_buffer_size-1);
582 rBOIL_END },
583 {"config/", rNoDefaults
584 rDoc("Top Level Application Configuration Parameters"),
585 &Config::ports, [](const char *, rtosc::RtData &d){d.forward();}},
586 {"presets/", rDoc("Parameter Presets"), &preset_ports, rBOIL_BEGIN
587 SNIP
588 preset_ports.dispatch(msg, data);
589 rBOIL_END},
590 {"watch/", rDoc("Interface to grab out live synthesis state"), &watchPorts,
591 rBOIL_BEGIN;
592 SNIP;
593 watchPorts.dispatch(msg, data);
594 rBOIL_END},
595 {"bank/", rDoc("Controls for instrument banks"), &bankPorts,
596 [](const char*,RtData&) {}},
597 {"learn:s", rProp(deprecated) rDoc("MIDI Learn"), 0,
598 rBegin;
599 int free_slot = m->automate.free_slot();
600 if(free_slot >= 0) {
601 m->automate.createBinding(free_slot, rtosc_argument(msg, 0).s, true);
602 m->automate.active_slot = free_slot;
604 rEnd},
607 #undef rBegin
608 #undef rEnd
610 const Ports &Master::ports = master_ports;
612 class DataObj:public rtosc::RtData
614 public:
615 DataObj(char *loc_, size_t loc_size_, void *obj_, rtosc::ThreadLink *bToU_)
617 memset(loc_, 0, loc_size_);
618 loc = loc_;
619 loc_size = loc_size_;
620 obj = obj_;
621 bToU = bToU_;
622 forwarded = false;
625 virtual void replyArray(const char *path, const char *args, rtosc_arg_t *vals) override
627 char *buffer = bToU->buffer();
628 rtosc_amessage(buffer,bToU->buffer_size(),path,args,vals);
629 reply(buffer);
631 virtual void reply(const char *path, const char *args, ...) override
633 va_list va;
634 va_start(va,args);
635 char *buffer = bToU->buffer();
636 rtosc_vmessage(buffer,bToU->buffer_size(),path,args,va);
637 reply(buffer);
638 va_end(va);
640 virtual void reply(const char *msg) override
642 if(rtosc_message_length(msg, -1) == 0)
643 fprintf(stderr, "Warning: Invalid Rtosc message '%s'\n", msg);
644 bToU->raw_write(msg);
646 virtual void broadcast(const char *path, const char *args, ...) override{
647 va_list va;
648 va_start(va,args);
649 reply("/broadcast", "");
650 char *buffer = bToU->buffer();
651 rtosc_vmessage(buffer,bToU->buffer_size(),path,args,va);
652 reply(buffer);
653 va_end(va);
655 virtual void broadcast(const char *msg) override
657 reply("/broadcast", "");
658 reply(msg);
661 virtual void forward(const char *reason) override
663 assert(message);
664 (void) reason;
665 reply("/forward", "");
666 printf("forwarding '%s'\n", message);
667 forwarded = true;
669 bool forwarded;
670 private:
671 rtosc::ThreadLink *bToU;
674 vuData::vuData(void)
675 :outpeakl(0.0f), outpeakr(0.0f), maxoutpeakl(0.0f), maxoutpeakr(0.0f),
676 rmspeakl(0.0f), rmspeakr(0.0f), clipped(0)
679 void Master::saveAutomation(XMLwrapper &xml, const rtosc::AutomationMgr &midi)
681 xml.beginbranch("automation");
683 XmlNode metadata("mgr-info");
684 metadata["nslots"] = to_s(midi.nslots);
685 metadata["nautomations"] = to_s(midi.per_slot);
686 metadata["ncontrol"] = to_s(midi.slots[0].automations[0].map.npoints);
687 xml.add(metadata);
689 for(int i=0; i<midi.nslots; ++i) {
690 const auto &slot = midi.slots[i];
691 if(!slot.used)
692 continue;
693 xml.beginbranch("slot", i);
694 XmlNode params("params");
695 params["midi-cc"] = to_s(slot.midi_cc);
696 params["name"] = to_s(slot.name);
697 xml.add(params);
698 for(int j=0; j<midi.per_slot; ++j) {
699 const auto &au = slot.automations[j];
700 if(!au.used)
701 continue;
702 xml.beginbranch("automation", j);
703 XmlNode automation("params");
704 automation["path"] = au.param_path;
705 XmlNode mapping("mapping");
706 mapping["gain"] = to_s(au.map.gain);
707 mapping["offset"] = to_s(au.map.offset);
708 xml.add(automation);
709 xml.add(mapping);
710 xml.endbranch();
713 xml.endbranch();
716 xml.endbranch();
719 void Master::loadAutomation(XMLwrapper &xml, rtosc::AutomationMgr &midi)
721 //Clear out old data
722 for(int i=0; i<midi.nslots; ++i)
723 midi.clearSlot(i);
725 if(xml.enterbranch("automation")) {
726 for(int i=0; i<midi.nslots; ++i) {
727 auto &slot = midi.slots[i];
728 if(xml.enterbranch("slot", i)) {
729 for(int j=0; j<midi.per_slot; ++j) {
730 if(xml.enterbranch("automation", j)) {
731 float gain = 1.0;
732 float offset = 0.0;
733 std::string path = "";
734 for(auto node:xml.getBranch()) {
735 if(node.name == "params")
736 path = node["path"];
737 else if(node.name == "mapping") {
738 gain = atof(node["gain"].c_str());
739 offset = atof(node["offset"].c_str());
742 printf("createBinding(%d, %s, false)\n", i, path.c_str());
743 midi.createBinding(i, path.c_str(), false);
744 midi.setSlotSubGain(i, j, gain);
745 midi.setSlotSubOffset(i, j, offset);
746 midi.updateMapping(i, j);
747 xml.exitbranch();
750 for(auto node:xml.getBranch())
752 if(node.name == "params")
754 slot.midi_cc = atoi(node["midi-cc"].c_str());
755 if(node["name"] != "")
757 strncpy(slot.name, node["name"].c_str(), sizeof(slot.name) - 1);
761 xml.exitbranch();
764 xml.exitbranch();
768 Master::Master(const SYNTH_T &synth_, Config* config)
769 :HDDRecorder(synth_), time(synth_), ctl(synth_, &time),
770 microtonal(config->cfg.GzipCompression), bank(config),
771 automate(16,4,8),
772 frozenState(false), pendingMemory(false),
773 synth(synth_), gzip_compression(config->cfg.GzipCompression)
775 SaveFullXml=(config->cfg.SaveFullXml==1);
776 bToU = NULL;
777 uToB = NULL;
779 // set default tempo
780 time.tempo = 120;
782 //Setup MIDI Learn
783 automate.set_ports(master_ports);
784 automate.set_instance(this);
785 midi.frontend = [this](const char *msg) {bToU->raw_write(msg);};
786 midi.backend = [this](const char *msg) {applyOscEvent(msg);};
787 automate.backend = [this](const char *msg) {applyOscEvent(msg);};
789 memory = new AllocatorClass();
790 swaplr = 0;
791 off = 0;
792 smps = 0;
793 bufl = new float[synth.buffersize];
794 bufr = new float[synth.buffersize];
796 last_xmz[0] = 0;
797 fft = new FFTwrapper(synth.oscilsize);
799 shutup = 0;
800 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
801 vuoutpeakpartl[npart] = 1e-9;
802 vuoutpeakpartr[npart] = 1e-9;
803 fakepeakpart[npart] = 0;
807 ScratchString ss;
808 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
810 part[npart] = new Part(*memory, synth, time, config->cfg.GzipCompression,
811 config->cfg.Interpolation, &microtonal, fft, &watcher,
812 (ss+"/part"+npart+"/").c_str);
813 smoothing_part_l[npart].sample_rate( synth.samplerate );
814 smoothing_part_l[npart].reset_on_next_apply( true ); /* necessary to make CI tests happy, otherwise of no practical use */
815 smoothing_part_r[npart].sample_rate( synth.samplerate );
816 smoothing_part_r[npart].reset_on_next_apply( true ); /* necessary to make CI tests happy, otherwise of no practical use */
819 smoothing.sample_rate( synth.samplerate );
820 smoothing.reset_on_next_apply( true ); /* necessary to make CI tests happy, otherwise of no practical use */
822 //Insertion Effects init
823 for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
824 insefx[nefx] = new EffectMgr(*memory, synth, 1, &time);
826 //System Effects init
827 for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx)
828 sysefx[nefx] = new EffectMgr(*memory, synth, 0, &time);
830 //Note Visualization
831 memset(activeNotes, 0, sizeof(activeNotes));
833 defaults();
835 mastercb = 0;
836 mastercb_ptr = 0;
839 bool Master::applyOscEvent(const char *msg, float *outl, float *outr,
840 bool offline, bool nio, DataObj& d, int msg_id,
841 Master* master_from_mw)
843 if(!strcmp(msg, "/load-master")) {
844 Master *this_master = master_from_mw ? master_from_mw : this;
845 Master *new_master = *(Master**)rtosc_argument(msg, 0).b.data;
846 // This can not fail anymore, but just to be sure...
847 assert(new_master != this_master);
850 * WARNING: Do not use anything from "this" below, use "this_master"
853 if(!offline)
854 new_master->AudioOut(outl, outr);
855 if(nio)
856 Nio::masterSwap(new_master);
857 if (this_master->hasMasterCb()) {
858 this_master->mastercb(this_master->mastercb_ptr, new_master);
860 bToU->write("/free", "sb", "Master", sizeof(Master*), &this_master);
861 return false;
862 } else if(!strcmp(msg, "/switch-master")) {
863 // if the other stuff from load-master is needed optionally
864 // (currently, it is not needed anywhere)
865 // add booleans to the parameters of "/switch-master"
866 Master *new_master = *(Master**)rtosc_argument(msg, 0).b.data;
867 if (hasMasterCb())
868 mastercb(mastercb_ptr, new_master);
869 return false;
872 //XXX yes, this is not realtime safe, but it is useful...
873 if(strcmp(msg, "/get-vu") && false) {
874 fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 5 + 30, 0 + 40);
875 if(msg_id > 0)
876 fprintf(stdout, "backend[%d]: '%s'<%s>\n", msg_id, msg,
877 rtosc_argument_string(msg));
878 else
879 fprintf(stdout, "backend[*]: '%s'<%s>\n", msg,
880 rtosc_argument_string(msg));
881 fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
884 ports.dispatch(msg, d, true);
886 if(!d.matches) {
887 //workaround for requesting voice status
888 int a=0, b=0, c=0;
889 char e=0;
890 if(4 == sscanf(msg, "/part%d/kit%d/adpars/VoicePar%d/Enable%c", &a, &b, &c, &e)) {
891 d.reply(msg, "F");
892 d.matches++;
895 if(!d.matches && !d.forwarded) {// && !ports.apropos(msg)) {
896 fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 1, 7 + 30, 0 + 40);
897 fprintf(stderr, "Unknown address<BACKEND:%s> '%s:%s'\n",
898 offline ? "offline" : "online",
899 uToB->peak(),
900 rtosc_argument_string(uToB->peak()));
901 fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
903 else if(d.forwarded)
904 bToU->raw_write(msg);
906 return true;
909 bool Master::applyOscEvent(const char *msg, float *outl, float *outr,
910 bool offline, bool nio, int msg_id)
912 char loc_buf[1024];
913 DataObj d{loc_buf, 1024, this, bToU};
914 memset(loc_buf, 0, sizeof(loc_buf));
915 d.matches = 0;
917 return applyOscEvent(msg, outl, outr, offline, nio, d, msg_id);
920 bool Master::applyOscEvent(const char *msg, bool nio, int msg_id)
922 return applyOscEvent(msg, NULL, NULL, true, nio, msg_id);
925 void Master::defaults()
927 union {float f; uint32_t i;} convert;
928 convert.i = 0xC0D55556;
929 Volume = convert.f;
930 setPkeyshift(64);
932 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
933 part[npart]->defaults();
934 part[npart]->partno = npart % NUM_MIDI_CHANNELS;
935 part[npart]->Prcvchn = npart % NUM_MIDI_CHANNELS;
938 partonoff(0, 1); //enable the first part
940 for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) {
941 insefx[nefx]->defaults();
942 Pinsparts[nefx] = -1;
945 //System Effects init
946 for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) {
947 sysefx[nefx]->defaults();
948 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
949 setPsysefxvol(npart, nefx, 0);
951 for(int nefxto = 0; nefxto < NUM_SYS_EFX; ++nefxto)
952 setPsysefxsend(nefx, nefxto, 0);
955 microtonal.defaults();
956 ShutUp();
960 * Note On Messages (velocity=0 for NoteOff)
962 void Master::noteOn(char chan, note_t note, char velocity, float note_log2_freq)
964 if(velocity) {
965 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
966 if(chan == part[npart]->Prcvchn) {
967 fakepeakpart[npart] = velocity * 2;
968 if(part[npart]->Penabled)
969 part[npart]->NoteOn(note, velocity, keyshift, note_log2_freq);
972 activeNotes[note] = 1;
973 HDDRecorder.triggernow();
975 else
976 this->noteOff(chan, note);
980 * Note Off Messages
982 void Master::noteOff(char chan, note_t note)
984 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
985 if((chan == part[npart]->Prcvchn) && part[npart]->Penabled)
986 part[npart]->NoteOff(note);
987 activeNotes[note] = 0;
991 * Pressure Messages (velocity=0 for NoteOff)
993 void Master::polyphonicAftertouch(char chan, note_t note, char velocity)
995 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
996 if(chan == part[npart]->Prcvchn)
997 if(part[npart]->Penabled)
998 part[npart]->PolyphonicAftertouch(note, velocity);
1002 * Controllers
1004 void Master::setController(char chan, int type, int par)
1006 if(frozenState)
1007 return;
1008 automate.handleMidi(chan, type, par);
1009 midi.handleCC(type, par, chan, false);
1010 if((type == C_dataentryhi) || (type == C_dataentrylo)
1011 || (type == C_nrpnhi) || (type == C_nrpnlo)) { //Process RPN and NRPN by the Master (ignore the chan)
1012 ctl.setparameternumber(type, par);
1014 int parhi = -1, parlo = -1, valhi = -1, vallo = -1;
1015 if(ctl.getnrpn(&parhi, &parlo, &valhi, &vallo) == 0) { //this is NRPN
1016 switch(parhi) {
1017 case 0x04: //System Effects
1018 if(parlo < NUM_SYS_EFX)
1019 sysefx[parlo]->seteffectparrt(valhi, vallo);
1020 break;
1021 case 0x08: //Insertion Effects
1022 if(chan == 0 && parlo < NUM_INS_EFX)
1023 insefx[parlo]->seteffectparrt(valhi, vallo);
1024 else if (chan < NUM_MIDI_PARTS && parlo < NUM_PART_EFX)
1025 part[chan-1]->partefx[parlo]->seteffectparrt(valhi, vallo);
1026 break;
1027 default:
1028 midi.handleCC(parhi<<7&parlo,valhi<<7&vallo, chan, true);
1029 break;
1032 } else { //other controllers
1033 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) //Send the controller to all part assigned to the channel
1034 if((chan == part[npart]->Prcvchn) && (part[npart]->Penabled != 0))
1035 part[npart]->SetController(type, par);
1037 if(type == C_allsoundsoff) { //cleanup insertion/system FX
1038 for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx)
1039 sysefx[nefx]->cleanup();
1040 for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
1041 insefx[nefx]->cleanup();
1047 * Per note controllers
1049 void Master::setController(char chan, int type, note_t note, float value)
1051 if(frozenState)
1052 return;
1054 /* Send the controller to all part assigned to the channel */
1055 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
1056 if((chan == part[npart]->Prcvchn) && (part[npart]->Penabled != 0))
1057 part[npart]->SetController(type, note, value, keyshift);
1060 void Master::vuUpdate(const float *outl, const float *outr)
1062 //Peak computation (for vumeters)
1063 vu.outpeakl = 1e-12;
1064 vu.outpeakr = 1e-12;
1065 for(int i = 0; i < synth.buffersize; ++i) {
1066 if(fabsf(outl[i]) > vu.outpeakl)
1067 vu.outpeakl = fabsf(outl[i]);
1068 if(fabsf(outr[i]) > vu.outpeakr)
1069 vu.outpeakr = fabsf(outr[i]);
1071 if((vu.outpeakl > 1.0f) || (vu.outpeakr > 1.0f))
1072 vu.clipped = 1;
1073 if(vu.maxoutpeakl < vu.outpeakl)
1074 vu.maxoutpeakl = vu.outpeakl;
1075 if(vu.maxoutpeakr < vu.outpeakr)
1076 vu.maxoutpeakr = vu.outpeakr;
1078 //RMS Peak computation (for vumeters)
1079 vu.rmspeakl = 1e-12;
1080 vu.rmspeakr = 1e-12;
1081 for(int i = 0; i < synth.buffersize; ++i) {
1082 vu.rmspeakl += outl[i] * outl[i];
1083 vu.rmspeakr += outr[i] * outr[i];
1085 vu.rmspeakl = sqrt(vu.rmspeakl / synth.buffersize_f);
1086 vu.rmspeakr = sqrt(vu.rmspeakr / synth.buffersize_f);
1088 //Part Peak computation (for Part vumeters or fake part vumeters)
1089 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
1090 vuoutpeakpartl[npart] = 1.0e-12f;
1091 vuoutpeakpartr[npart] = 1.0e-12f;
1092 if(part[npart]->Penabled != 0) {
1093 float *outl = part[npart]->partoutl,
1094 *outr = part[npart]->partoutr;
1095 for(int i = 0; i < synth.buffersize; ++i) {
1096 if (fabsf(outl[i]) > vuoutpeakpartl[npart])
1097 vuoutpeakpartl[npart] = fabsf(outl[i]);
1098 if (fabsf(outr[i]) > vuoutpeakpartr[npart])
1099 vuoutpeakpartr[npart] = fabsf(outr[i]);
1102 else
1103 if(fakepeakpart[npart] > 1)
1104 fakepeakpart[npart]--;
1109 * Enable/Disable a part
1111 void Master::partonoff(int npart, int what)
1113 if(npart >= NUM_MIDI_PARTS)
1114 return;
1115 if(what == 0) { //disable part
1116 fakepeakpart[npart] = 0;
1117 part[npart]->Penabled = 0;
1118 part[npart]->cleanup();
1119 for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) {
1120 if(Pinsparts[nefx] == npart)
1121 insefx[nefx]->cleanup();
1124 else { //enabled
1125 part[npart]->Penabled = 1;
1126 fakepeakpart[npart] = 0;
1130 void Master::setMasterChangedCallback(void(*cb)(void*,Master*), void *ptr)
1132 mastercb = cb;
1133 mastercb_ptr = ptr;
1136 void Master::copyMasterCbTo(Master *dest)
1138 dest->mastercb = mastercb;
1139 dest->mastercb_ptr = mastercb_ptr;
1142 bool Master::hasMasterCb() const
1144 return !!mastercb;
1147 void Master::setAudioCompressor(bool enabled)
1149 Nio::setAudioCompressor(enabled);
1152 #if 0
1153 template <class T>
1154 struct def_skip
1156 static void skip(const char*& argptr) { argptr += sizeof(T); }
1159 template <class T>
1160 struct str_skip
1162 static void skip(const char*& argptr) { while(argptr++); /*TODO: 4 padding */ }
1165 template<class T, class Display = T, template<class TMP> class SkipsizeFunc = def_skip>
1166 void _dump_prim_arg(const char*& argptr, std::ostream& os)
1168 os << ' ' << (Display)*(const T*)argptr;
1169 SkipsizeFunc<T>::skip(argptr);
1172 void dump_msg(const char* ptr, std::ostream& os = std::cerr)
1174 assert(*ptr == '/');
1175 os << ptr;
1177 while(*++ptr) ; // skip address
1178 while(!*++ptr) ; // skip 0s
1180 assert(*ptr == ',');
1181 os << ' ' << (ptr + 1);
1183 const char* argptr = ptr;
1184 while(*++argptr) ; // skip type string
1185 while(!*++argptr) ; // skip 0s
1187 char c;
1188 while((c = *++ptr))
1190 switch(c)
1192 case 'i':
1193 _dump_prim_arg<int32_t>(argptr, os); break;
1194 case 'c':
1195 _dump_prim_arg<int32_t, char>(argptr, os); break;
1196 // case 's':
1197 // _dump_prim_arg<char, const char*>(argptr, os); break;
1198 default:
1199 exit(1);
1204 #endif
1205 int msg_id=0;
1207 bool Master::runOSC(float *outl, float *outr, bool offline,
1208 Master* master_from_mw)
1210 // the following block is only ever entered by 1 thread at a time
1211 // other threads have to ignore it
1212 if(!run_osc_in_use.exchange(true)) // exchange returns value before call
1215 * WARNING: Do not return without "run_osc_in_use.store(false)"
1218 //Handle user events
1219 char loc_buf[1024];
1220 DataObj d{loc_buf, 1024, this, bToU};
1221 memset(loc_buf, 0, sizeof(loc_buf));
1223 int events = 0;
1224 for(; uToB && uToB->hasNext() && events < 100; ++msg_id, ++events)
1226 const char *msg = uToB->read();
1227 if(! applyOscEvent(msg, outl, outr, offline, true, d, msg_id,
1228 master_from_mw) )
1230 run_osc_in_use.store(false);
1231 return false;
1235 if(automate.damaged) {
1236 d.broadcast("/damage", "s", "/automate/");
1237 automate.damaged = 0;
1240 if(events>1 && false)
1241 fprintf(stderr, "backend: %d events per cycle\n",events);
1243 run_osc_in_use.store(false);
1244 return true;
1246 else { return true; /* = no new master */ }
1250 * Master audio out (the final sound)
1252 bool Master::AudioOut(float *outl, float *outr)
1254 //Danger Limits
1255 if(memory->lowMemory(2,1024*1024))
1256 printf("QUITE LOW MEMORY IN THE RT POOL BE PREPARED FOR WEIRD BEHAVIOR!!\n");
1257 //Normal Limits
1258 if(!pendingMemory && memory->lowMemory(4,1024*1024)) {
1259 printf("Requesting more memory\n");
1260 bToU->write("/request-memory", "");
1261 pendingMemory = true;
1264 //work through events
1265 if(!runOSC(outl, outr, false))
1266 return false;
1269 //Handle watch points
1270 if(bToU)
1271 watcher.write_back = bToU;
1272 watcher.tick();
1275 //Swaps the Left channel with Right Channel
1276 if(swaplr)
1277 swap(outl, outr);
1279 //clean up the output samples (should not be needed?)
1280 memset(outl, 0, synth.bufferbytes);
1281 memset(outr, 0, synth.bufferbytes);
1283 //Compute part samples and store them part[npart]->partoutl,partoutr
1284 //Note: We do this regardless if the part is enabled or not, to allow
1285 //the part to graciously shut down when disabled.
1286 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
1287 part[npart]->ComputePartSmps();
1289 //Insertion effects
1290 for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
1291 if(Pinsparts[nefx] >= 0) {
1292 int efxpart = Pinsparts[nefx];
1293 if(part[efxpart]->Penabled)
1294 insefx[nefx]->out(part[efxpart]->partoutl,
1295 part[efxpart]->partoutr);
1299 float gainbuf[synth.buffersize];
1301 //Apply the part volumes and pannings (after insertion effects)
1302 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
1303 if(!part[npart]->Penabled)
1304 continue;
1306 Stereo<float> newvol(part[npart]->gain);
1308 float pan = part[npart]->panning;
1309 if(pan < 0.5f)
1310 newvol.r *= pan * 2.0f;
1311 else
1312 newvol.l *= (1.0f - pan) * 2.0f;
1313 //if(npart==0)
1314 //printf("[%d]vol = %f->%f\n", npart, oldvol.l, newvol.l);
1318 /* This is where the part volume (and pan) smoothing and application happens */
1319 if ( smoothing_part_l[npart].apply( gainbuf, synth.buffersize, newvol.l ) )
1321 for ( int i = 0; i < synth.buffersize; ++i )
1322 part[npart]->partoutl[i] *= gainbuf[i];
1324 else
1326 for ( int i = 0; i < synth.buffersize; ++i )
1327 part[npart]->partoutl[i] *= newvol.l;
1330 if ( smoothing_part_r[npart].apply( gainbuf, synth.buffersize, newvol.r ) )
1332 for ( int i = 0; i < synth.buffersize; ++i )
1333 part[npart]->partoutr[i] *= gainbuf[i];
1335 else
1337 for ( int i = 0; i < synth.buffersize; ++i )
1338 part[npart]->partoutr[i] *= newvol.r;
1342 //System effects
1343 for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) {
1344 if(sysefx[nefx]->geteffect() == 0)
1345 continue; //the effect is disabled
1347 float tmpmixl[synth.buffersize];
1348 float tmpmixr[synth.buffersize];
1349 //Clean up the samples used by the system effects
1350 memset(tmpmixl, 0, synth.bufferbytes);
1351 memset(tmpmixr, 0, synth.bufferbytes);
1353 //Mix the channels according to the part settings about System Effect
1354 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
1355 //skip if the part has no output to effect
1356 if(Psysefxvol[nefx][npart] == 0)
1357 continue;
1359 //skip if the part is disabled
1360 if(part[npart]->Penabled == 0)
1361 continue;
1363 //the output volume of each part to system effect
1364 const float vol = sysefxvol[nefx][npart];
1365 for(int i = 0; i < synth.buffersize; ++i) {
1366 tmpmixl[i] += part[npart]->partoutl[i] * vol;
1367 tmpmixr[i] += part[npart]->partoutr[i] * vol;
1371 // system effect send to next ones
1372 for(int nefxfrom = 0; nefxfrom < nefx; ++nefxfrom)
1373 if(Psysefxsend[nefxfrom][nefx] != 0) {
1374 const float vol = sysefxsend[nefxfrom][nefx];
1375 for(int i = 0; i < synth.buffersize; ++i) {
1376 tmpmixl[i] += sysefx[nefxfrom]->efxoutl[i] * vol;
1377 tmpmixr[i] += sysefx[nefxfrom]->efxoutr[i] * vol;
1381 sysefx[nefx]->out(tmpmixl, tmpmixr);
1383 //Add the System Effect to sound output
1384 const float outvol = sysefx[nefx]->sysefxgetvolume();
1385 for(int i = 0; i < synth.buffersize; ++i) {
1386 outl[i] += tmpmixl[i] * outvol;
1387 outr[i] += tmpmixr[i] * outvol;
1391 //Mix all parts
1392 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
1393 if(part[npart]->Penabled) //only mix active parts
1394 for(int i = 0; i < synth.buffersize; ++i) { //the volume did not changed
1395 outl[i] += part[npart]->partoutl[i];
1396 outr[i] += part[npart]->partoutr[i];
1399 //Insertion effects for Master Out
1400 for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
1401 if(Pinsparts[nefx] == -2)
1402 insefx[nefx]->out(outl, outr);
1404 float vol = dB2rap(Volume);
1406 //Master Volume
1407 /* this is where the master volume smoothing and application happens */
1408 if ( smoothing.apply( gainbuf, synth.buffersize, vol ) )
1410 for ( int i = 0; i < synth.buffersize; ++i )
1412 outl[i] *= gainbuf[i];
1413 outr[i] *= gainbuf[i];
1416 else
1418 for ( int i = 0; i < synth.buffersize; ++i )
1420 outl[i] *= vol;
1421 outr[i] *= vol;
1425 vuUpdate(outl, outr);
1427 //Shutup if it is asked (with fade-out)
1428 if(shutup) {
1429 for(int i = 0; i < synth.buffersize; ++i) {
1430 float tmp = (synth.buffersize_f - i) / synth.buffersize_f;
1431 outl[i] *= tmp;
1432 outr[i] *= tmp;
1434 ShutUp();
1437 //update the global frame timer
1438 time++;
1440 #ifdef DEMO_VERSION
1441 double seconds = time.time()*synth.buffersize_f/synth.samplerate_f;
1442 if(seconds > 10*60) {//10 minute trial
1443 shutup = true;
1444 for(int i = 0; i < synth.buffersize; ++i) {
1445 outl[i] *= 0.0f;
1446 outr[i] *= 0.0f;
1449 #endif
1451 //Update pulse
1452 last_ack = last_beat;
1455 return true;
1458 //TODO review the respective code from yoshimi for this
1459 //If memory serves correctly, libsamplerate was used
1460 void Master::GetAudioOutSamples(size_t nsamples,
1461 unsigned samplerate,
1462 float *outl,
1463 float *outr)
1465 off_t out_off = 0;
1467 //Fail when resampling rather than doing a poor job
1468 if(synth.samplerate != samplerate) {
1469 printf("darn it: %d vs %d\n", synth.samplerate, samplerate);
1470 return;
1473 while(nsamples) {
1474 //use all available samples
1475 if(nsamples >= smps) {
1476 memcpy(outl + out_off, bufl + off, sizeof(float) * smps);
1477 memcpy(outr + out_off, bufr + off, sizeof(float) * smps);
1478 nsamples -= smps;
1480 //generate samples
1481 if (! AudioOut(bufl, bufr))
1482 return;
1484 off = 0;
1485 out_off += smps;
1486 smps = synth.buffersize;
1488 else { //use some samples
1489 memcpy(outl + out_off, bufl + off, sizeof(float) * nsamples);
1490 memcpy(outr + out_off, bufr + off, sizeof(float) * nsamples);
1491 smps -= nsamples;
1492 off += nsamples;
1493 nsamples = 0;
1498 Master::~Master()
1500 delete []bufl;
1501 delete []bufr;
1503 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
1504 delete part[npart];
1505 for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
1506 delete insefx[nefx];
1507 for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx)
1508 delete sysefx[nefx];
1510 delete fft;
1511 delete memory;
1516 * Parameter control
1519 float Master::volume127ToFloat(unsigned char volume_)
1521 return (volume_ - 96.0f) / 96.0f * 40.0;
1524 void Master::setPkeyshift(char Pkeyshift_)
1526 Pkeyshift = Pkeyshift_;
1527 keyshift = (int)Pkeyshift - 64;
1531 void Master::setPsysefxvol(int Ppart, int Pefx, char Pvol)
1533 Psysefxvol[Pefx][Ppart] = Pvol;
1534 sysefxvol[Pefx][Ppart] = powf(0.1f, (1.0f - Pvol / 96.0f) * 2.0f);
1537 void Master::setPsysefxsend(int Pefxfrom, int Pefxto, char Pvol)
1539 Psysefxsend[Pefxfrom][Pefxto] = Pvol;
1540 sysefxsend[Pefxfrom][Pefxto] = powf(0.1f, (1.0f - Pvol / 96.0f) * 2.0f);
1545 * Panic! (Clean up all parts and effects)
1547 void Master::ShutUp()
1549 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
1550 part[npart]->cleanup();
1551 fakepeakpart[npart] = 0;
1553 for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
1554 insefx[nefx]->cleanup();
1555 for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx)
1556 sysefx[nefx]->cleanup();
1557 memset(activeNotes, 0, sizeof(activeNotes));
1558 vuresetpeaks();
1559 shutup = 0;
1564 * Reset peaks and clear the "cliped" flag (for VU-meter)
1566 void Master::vuresetpeaks()
1568 vu.outpeakl = 1e-9;
1569 vu.outpeakr = 1e-9;
1570 vu.maxoutpeakl = 1e-9;
1571 vu.maxoutpeakr = 1e-9;
1572 vu.clipped = 0;
1575 void Master::applyparameters(void)
1577 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
1578 part[npart]->applyparameters();
1581 void Master::initialize_rt(void)
1583 for(int i=0; i<NUM_SYS_EFX; ++i)
1584 sysefx[i]->init();
1585 for(int i=0; i<NUM_INS_EFX; ++i)
1586 insefx[i]->init();
1588 for(int i=0; i<NUM_MIDI_PARTS; ++i)
1589 part[i]->initialize_rt();
1592 void Master::add2XML(XMLwrapper& xml)
1594 xml.addparreal("volume", Volume);
1595 xml.addpar("key_shift", Pkeyshift);
1596 xml.addparbool("nrpn_receive", ctl.NRPN.receive);
1598 xml.beginbranch("MICROTONAL");
1599 microtonal.add2XML(xml);
1600 xml.endbranch();
1602 if (SaveFullXml) {
1603 xml.SaveFullXml=true; // save disabled parts
1604 xml.minimal=false;
1607 saveAutomation(xml, automate);
1608 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
1609 xml.beginbranch("PART", npart);
1610 part[npart]->add2XML(xml);
1611 xml.endbranch();
1614 xml.beginbranch("SYSTEM_EFFECTS");
1615 for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) {
1616 xml.beginbranch("SYSTEM_EFFECT", nefx);
1617 xml.beginbranch("EFFECT");
1618 sysefx[nefx]->add2XML(xml);
1619 xml.endbranch();
1621 for(int pefx = 0; pefx < NUM_MIDI_PARTS; ++pefx) {
1622 xml.beginbranch("VOLUME", pefx);
1623 xml.addpar("vol", Psysefxvol[nefx][pefx]);
1624 xml.endbranch();
1627 for(int tonefx = nefx + 1; tonefx < NUM_SYS_EFX; ++tonefx) {
1628 xml.beginbranch("SENDTO", tonefx);
1629 xml.addpar("send_vol", Psysefxsend[nefx][tonefx]);
1630 xml.endbranch();
1634 xml.endbranch();
1636 xml.endbranch();
1638 xml.beginbranch("INSERTION_EFFECTS");
1639 for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) {
1640 xml.beginbranch("INSERTION_EFFECT", nefx);
1641 xml.addpar("part", Pinsparts[nefx]);
1643 xml.beginbranch("EFFECT");
1644 insefx[nefx]->add2XML(xml);
1645 xml.endbranch();
1646 xml.endbranch();
1649 xml.endbranch();
1653 int Master::getalldata(char **data)
1655 XMLwrapper xml;
1657 xml.beginbranch("MASTER");
1659 add2XML(xml);
1661 xml.endbranch();
1663 *data = xml.getXMLdata();
1664 return strlen(*data) + 1;
1667 void Master::putalldata(const char *data)
1669 XMLwrapper xml;
1670 if(!xml.putXMLdata(data)) {
1671 return;
1674 if(xml.enterbranch("MASTER") == 0)
1675 return;
1677 getfromXML(xml);
1679 xml.exitbranch();
1682 int Master::saveXML(const char *filename)
1684 XMLwrapper xml;
1686 xml.beginbranch("MASTER");
1687 add2XML(xml);
1688 xml.endbranch();
1690 return xml.saveXMLfile(filename, gzip_compression);
1694 int Master::loadXML(const char *filename)
1696 XMLwrapper xml;
1698 if(xml.loadXMLfile(filename) < 0) {
1699 return -1;
1702 if(xml.enterbranch("MASTER") == 0)
1703 return -10;
1705 getfromXML(xml);
1706 xml.exitbranch();
1708 initialize_rt();
1709 return 0;
1712 void Master::getfromXML(XMLwrapper& xml)
1714 if (xml.hasparreal("volume")) {
1715 Volume = xml.getparreal("volume", Volume);
1716 } else {
1717 Volume = volume127ToFloat(xml.getpar127("volume", 0));
1719 setPkeyshift(xml.getpar127("key_shift", Pkeyshift));
1720 ctl.NRPN.receive = xml.getparbool("nrpn_receive", ctl.NRPN.receive);
1723 part[0]->Penabled = 0;
1724 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
1725 if(xml.enterbranch("PART", npart) == 0)
1726 continue;
1727 part[npart]->getfromXML(xml);
1728 xml.exitbranch();
1731 if(xml.enterbranch("MICROTONAL")) {
1732 microtonal.getfromXML(xml);
1733 xml.exitbranch();
1736 loadAutomation(xml, automate);
1738 sysefx[0]->changeeffect(0);
1739 if(xml.enterbranch("SYSTEM_EFFECTS")) {
1740 for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) {
1741 if(xml.enterbranch("SYSTEM_EFFECT", nefx) == 0)
1742 continue;
1743 if(xml.enterbranch("EFFECT")) {
1744 sysefx[nefx]->getfromXML(xml);
1745 xml.exitbranch();
1748 for(int partefx = 0; partefx < NUM_MIDI_PARTS; ++partefx) {
1749 if(xml.enterbranch("VOLUME", partefx) == 0)
1750 continue;
1751 setPsysefxvol(partefx, nefx,
1752 xml.getpar127("vol", Psysefxvol[partefx][nefx]));
1753 xml.exitbranch();
1756 for(int tonefx = nefx + 1; tonefx < NUM_SYS_EFX; ++tonefx) {
1757 if(xml.enterbranch("SENDTO", tonefx) == 0)
1758 continue;
1759 setPsysefxsend(nefx, tonefx,
1760 xml.getpar127("send_vol",
1761 Psysefxsend[nefx][tonefx]));
1762 xml.exitbranch();
1764 xml.exitbranch();
1766 xml.exitbranch();
1770 if(xml.enterbranch("INSERTION_EFFECTS")) {
1771 for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) {
1772 if(xml.enterbranch("INSERTION_EFFECT", nefx) == 0)
1773 continue;
1774 Pinsparts[nefx] = xml.getpar("part",
1775 Pinsparts[nefx],
1777 NUM_MIDI_PARTS);
1778 if(xml.enterbranch("EFFECT")) {
1779 insefx[nefx]->getfromXML(xml);
1780 xml.exitbranch();
1782 xml.exitbranch();
1785 xml.exitbranch();
1789 static rtosc_version version_in_rtosc_fmt()
1791 return rtosc_version
1793 (unsigned char) version.get_major(),
1794 (unsigned char) version.get_minor(),
1795 (unsigned char) version.get_revision()
1799 char* Master::getXMLData()
1801 XMLwrapper xml;
1803 xml.beginbranch("MASTER");
1804 add2XML(xml);
1805 xml.endbranch();
1807 return xml.getXMLdata();
1810 // this is being called as a "read only op" directly by the MiddleWare thread;
1811 // note that the Master itself is frozen
1812 std::string Master::saveOSC(std::string savefile)
1814 return rtosc::save_to_file(ports, this,
1815 nullptr, version_in_rtosc_fmt(), // both unused
1816 savefile);
1819 int Master::loadOSCFromStr(const char *file_content,
1820 savefile_dispatcher_t* dispatcher)
1822 return rtosc::load_from_file(file_content,
1823 ports, this,
1824 "ZynAddSubFX", version_in_rtosc_fmt(),
1825 dispatcher);
1828 string loadfile(string fname)
1830 std::ifstream t(fname.c_str());
1831 std::string str((std::istreambuf_iterator<char>(t)),
1832 std::istreambuf_iterator<char>());
1833 return str;
1836 int Master::loadOSC(const char *filename, savefile_dispatcher_t* dispatcher)
1838 int rval = loadOSCFromStr(loadfile(filename).c_str(), dispatcher);
1839 return rval < 0 ? rval : 0;