Fix oscdoc command line arg
[zynaddsubfx-code.git] / src / Misc / Master.cpp
blob07d024f4a8aa87365695a7e8a3a7199706b81c9b
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)
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);
73 //Now get the second index like normal
74 while(!isdigit(*m)) m++;
75 int ind2 = atoi(m);
76 Master &mast = *(Master*)d.obj;
78 if(rtosc_narguments(m)) {
79 mast.setPsysefxvol(ind2, ind1, rtosc_argument(m,0).i);
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",
89 rProp(parameter) 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))
111 master.setPsysefxsend(ind1, ind2, rtosc_argument(m,0).i);
112 else
113 d.reply(d.loc, "i", master.Psysefxsend[ind1][ind2]);
117 #define rBegin [](const char *msg, RtData &d) { rtosc::AutomationMgr &a = *(AutomationMgr*)d.obj
118 #define rEnd }
120 static int extract_num(const char *&msg)
122 while(*msg && !isdigit(*msg)) msg++;
123 int num = atoi(msg);
124 while(isdigit(*msg)) msg++;
125 return num;
128 static int get_next_int(const char *msg)
130 return extract_num(msg);
133 static const Ports mapping_ports = {
134 {"offset::f", rProp(parameter) rDefault(0) rShort("off") rLinear(-50, 50) rMap(unit, percent), 0,
135 rBegin;
136 int slot = d.idx[1];
137 int param = d.idx[0];
138 if(!strcmp("f",rtosc_argument_string(msg))) {
139 a.setSlotSubOffset(slot, param, rtosc_argument(msg, 0).f);
140 a.updateMapping(slot, param);
141 d.broadcast(d.loc, "f", a.getSlotSubOffset(slot, param));
142 } else
143 d.reply(d.loc, "f", a.getSlotSubOffset(slot, param));
144 rEnd},
145 {"gain::f", rProp(parameter) rDefault(100) rShort("gain") rLinear(-200, 200) rMap(unit, percent), 0,
146 rBegin;
147 int slot = d.idx[1];
148 int param = d.idx[0];
149 if(!strcmp("f",rtosc_argument_string(msg))) {
150 a.setSlotSubGain(slot, param, rtosc_argument(msg, 0).f);
151 a.updateMapping(slot, param);
152 d.broadcast(d.loc, "f", a.getSlotSubGain(slot, param));
153 } else
154 d.reply(d.loc, "f", a.getSlotSubGain(slot, param));
155 rEnd},
158 static const Ports auto_param_ports = {
159 {"used::T:F", rProp(parameter) rProp(read-only) rDoc("If automation is assigned to anything"), 0,
160 rBegin;
161 int slot = d.idx[1];
162 int param = d.idx[0];
164 d.reply(d.loc, a.slots[slot].automations[param].used ? "T" : "F");
165 rEnd},
166 {"active::T:F", rProp(parameter) rDoc("If automation is being actively used"), 0,
167 rBegin;
168 int slot = d.idx[1];
169 int param = d.idx[0];
170 if(rtosc_narguments(msg))
171 a.slots[slot].automations[param].active = rtosc_argument(msg, 0).T;
172 else
173 d.reply(d.loc, a.slots[slot].automations[param].active ? "T" : "F");
174 rEnd},
175 {"path::s", rProp(parameter) rProp(read-only) rDoc("Path of parameter"), 0,
176 rBegin;
177 int slot = d.idx[1];
178 int param = d.idx[0];
179 d.reply(d.loc, "s", a.slots[slot].automations[param].param_path);
180 rEnd},
181 {"clear:", rDoc("Clear automation param"), 0,
182 rBegin;
183 int slot = d.idx[1];
184 int param = d.idx[0];
185 a.clearSlotSub(slot, param);
186 rEnd},
187 {"mapping/", 0, &mapping_ports,
188 rBegin;
189 SNIP;
190 mapping_ports.dispatch(msg, d);
191 rEnd},
193 //{"mapping", rDoc("Parameter mapping control"), 0,
194 // rBegin;
195 // int slot = d.idx[1];
196 // int param = d.idx[0];
197 // if(!strcmp("b", rtosc_argument_string(msg))) {
198 // int len = rtosc_argument(msg, 0).b.len / sizeof(float);
199 // float *data = (float*)rtosc_argument(msg, 0).b.data;
200 // } else {
201 // d.reply(d.loc, "b",
202 // a.slots[slot].automations[param].map.npoints*sizeof(float),
203 // a.slots[slot].automations[param].map.control_points);
204 // }
205 // rEnd},
208 static const Ports slot_ports = {
209 //{"learn-binding:s", rDoc("Create binding for automation path with midi-learn"), 0,
210 // rBegin;
211 // (void) m;
212 // //m->automate.createBinding(rtosc_argument(msg, 0).i,
213 // // rtosc_argument(msg, 1).s,
214 // // rtosc_argument(msg, 2).T);
215 // rEnd},
216 //{"create-binding:s", rDoc("Create binding for automation path"), 0,
217 // rBegin;
218 // m->automate.createBinding(rtosc_argument(msg, 0).i,
219 // rtosc_argument(msg, 1).s,
220 // rtosc_argument(msg, 2).T);
221 // rEnd},
222 {"value::f", rProp(parameter) rMap(default, 0.5) rLinear(0, 1) rDoc("Access current value in slot 'i' (0..1)"), 0,
223 rBegin;
224 int num = d.idx[0];
225 if(!strcmp("f",rtosc_argument_string(msg))) {
226 a.setSlot(num, rtosc_argument(msg, 0).f);
227 d.broadcast(d.loc, "f", a.getSlot(num));
228 } else
229 d.reply(d.loc, "f", a.getSlot(num));
230 rEnd},
232 {"name::s", rProp(parameter) rDoc("Access name of automation slot"), 0,
233 rBegin;
234 int num = d.idx[0];
235 if(!strcmp("s",rtosc_argument_string(msg))) {
236 a.setName(num, rtosc_argument(msg, 0).s);
237 d.broadcast(d.loc, "s", a.getName(num));
238 } else
239 d.reply(d.loc, "s", a.getName(num));
240 rEnd},
241 {"midi-cc::i", rProp(parameter) rMap(default, -1) rDoc("Access assigned midi CC slot") , 0,
242 rBegin;
243 int slot = d.idx[0];
244 if(rtosc_narguments(msg))
245 a.slots[slot].midi_cc = rtosc_argument(msg, 0).i;
246 else
247 d.reply(d.loc, "i", a.slots[slot].midi_cc);
249 rEnd},
250 {"active::T:F", rProp(parameter) rMap(default, F) rDoc("If Slot is enabled"), 0,
251 rBegin;
252 int slot = d.idx[0];
253 if(rtosc_narguments(msg))
254 a.slots[slot].active = rtosc_argument(msg, 0).T;
255 else
256 d.reply(d.loc, a.slots[slot].active ? "T" : "F");
257 rEnd},
258 {"learning::i", rProp(parameter) rMap(default, -1) rDoc("If slot is trying to find a midi learn binding"), 0,
259 rBegin;
260 int slot = d.idx[0];
261 d.reply(d.loc, "i", a.slots[slot].learning);
262 rEnd},
263 {"clear:", rDoc("Clear automation slot"), 0,
264 rBegin;
265 int slot = d.idx[0];
266 a.clearSlot(slot);
267 rEnd},
268 {"param#4/", rDoc("Info on individual param mappings"), &auto_param_ports,
269 rBegin;
270 (void)a;
271 d.push_index(get_next_int(msg));
272 SNIP;
273 auto_param_ports.dispatch(msg, d);
274 d.pop_index();
275 rEnd},
278 static const Ports automate_ports = {
279 {"active-slot::i", rProp(parameter) rMap(min, -1) rMap(max, 16) rDoc("Active Slot for macro learning"), 0,
280 rBegin;
281 if(!strcmp("i",rtosc_argument_string(msg))) {
282 a.active_slot = rtosc_argument(msg, 0).i;
283 d.broadcast(d.loc, "i", a.active_slot);
284 } else
285 d.reply(d.loc, "i", a.active_slot);
286 rEnd},
287 {"learn-binding-new-slot:s", rDoc("Learn a parameter assigned to a new slot"), 0,
288 rBegin;
289 int free_slot = a.free_slot();
290 if(free_slot >= 0) {
291 a.createBinding(free_slot, rtosc_argument(msg, 0).s, true);
292 a.active_slot = free_slot;
294 rEnd},
295 {"learn-binding-same-slot:s", rDoc("Learn a parameter appending to the active-slot"), 0,
296 rBegin;
297 if(a.active_slot >= 0)
298 a.createBinding(a.active_slot, rtosc_argument(msg, 0).s, true);
299 rEnd},
300 // TODO: remove rNoWalk
301 {"slot#16/", rNoWalk rDoc("Parameters of individual automation slots"), &slot_ports,
302 rBegin;
303 (void)a;
304 d.push_index(get_next_int(msg));
305 SNIP;
306 slot_ports.dispatch(msg, d);
307 d.pop_index();
308 rEnd},
309 {"clear", rDoc("Clear all automation slots"), 0,
310 rBegin;
311 for(int i=0; i<a.nslots; ++i)
312 a.clearSlot(i);
313 rEnd},
314 {"load-blob:b", rProp(internal) rDoc("Load blob from middleware"), 0,
315 rBegin;
316 auto &b = **(rtosc::AutomationMgr **)rtosc_argument(msg, 0).b.data;
317 //XXX this code should likely be in rtosc
318 for(int i=0; i<a.nslots; ++i) {
319 auto &slota = a.slots[i];
320 auto &slotb = b.slots[i];
321 std::swap(slota.learning, slotb.learning);
322 std::swap(slota.midi_cc, slotb.midi_cc);
323 std::swap(slota.used, slotb.used);
324 std::swap(slota.active, slotb.active);
325 for(int j=0; j<a.per_slot; ++j) {
326 auto &aa = slota.automations[j];
327 auto &ab = slotb.automations[j];
328 std::swap(aa.used, ab.used);
329 std::swap(aa.active, ab.active);
330 std::swap(aa.param_path, ab.param_path);
331 std::swap(aa.param_min, ab.param_min);
332 std::swap(aa.param_max, ab.param_max);
333 std::swap(aa.param_step, ab.param_step);
334 std::swap(aa.param_type, ab.param_type);
335 std::swap(aa.map.offset, ab.map.offset);
336 std::swap(aa.map.gain, ab.map.gain);
337 std::swap(aa.map.upoints, ab.map.upoints);
338 for(int k=0; k<aa.map.npoints; ++k)
339 std::swap(aa.map.control_points[k], ab.map.control_points[k]);
342 rEnd},
345 #undef rBegin
346 #undef rEnd
347 #define rBegin [](const char *msg, RtData &d) { Master *m = (Master*)d.obj
348 #define rEnd }
350 static const Ports watchPorts = {
351 {"add:s", rDoc("Add synthesis state to watch"), 0,
352 rBegin;
353 m->watcher.add_watch(rtosc_argument(msg,0).s);
354 rEnd},
358 extern const Ports bankPorts;
359 static const Ports master_ports = {
360 rString(last_xmz, XMZ_PATH_MAX, "File name for last name loaded if any."),
361 rRecursp(part, 16, "Part"),//NUM_MIDI_PARTS
362 rRecursp(sysefx, 4, "System Effect"),//NUM_SYS_EFX
363 rRecursp(insefx, 8, "Insertion Effect"),//NUM_INS_EFX
364 rRecur(microtonal, "Microtonal Mapping Functionality"),
365 rRecur(ctl, "Controller"),
366 rArrayOption(Pinsparts, NUM_INS_EFX, rOpt(-2, Master), rOpt(-1, Off),
367 rOptions(Part1, Part2, Part3, Part4, Part5, Part6,
368 Part7, Part8, Part9, Part10, Part11, Part12,
369 Part13, Part14, Part15, Part16) rDefault(Off),
370 "Part to insert part onto"),
371 {"Pkeyshift::i", rShort("key shift") rProp(parameter) rLinear(0,127)
372 rDefault(64) rDoc("Global Key Shift"), 0, [](const char *m, RtData&d) {
373 if(rtosc_narguments(m)==0) {
374 d.reply(d.loc, "i", ((Master*)d.obj)->Pkeyshift);
375 } else if(rtosc_narguments(m)==1 && rtosc_type(m,0)=='i') {
376 ((Master*)d.obj)->setPkeyshift(limit<char>(rtosc_argument(m,0).i,0,127));
377 d.broadcast(d.loc, "i", ((Master*)d.obj)->Pkeyshift);}}},
378 {"echo", rDoc("Hidden port to echo messages"), 0, [](const char *m, RtData&d) {
379 d.reply(m-1);}},
380 {"get-vu:", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) {
381 Master *m = (Master*)d.obj;
382 d.reply("/vu-meter", "bb", sizeof(m->vu), &m->vu, sizeof(float)*NUM_MIDI_PARTS, m->vuoutpeakpart);}},
383 {"vu-meter:", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) {
384 Master *m = (Master*)d.obj;
385 char types[6+NUM_MIDI_PARTS+1] = {0};
386 rtosc_arg_t args[6+NUM_MIDI_PARTS+1];
387 for(int i=0; i<6+NUM_MIDI_PARTS; ++i)
388 types[i] = 'f';
389 args[0].f = m->vu.outpeakl;
390 args[1].f = m->vu.outpeakr;
391 args[2].f = m->vu.maxoutpeakl;
392 args[3].f = m->vu.maxoutpeakr;
393 args[4].f = m->vu.rmspeakl;
394 args[5].f = m->vu.rmspeakr;
395 for(int i=0; i<NUM_MIDI_PARTS; ++i)
396 args[6+i].f = m->vuoutpeakpart[i];
398 d.replyArray("/vu-meter", types, args);}},
399 {"reset-vu:", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) {
400 Master *m = (Master*)d.obj;
401 m->vuresetpeaks();}},
402 {"load-part:ib", rProp(internal) rDoc("Load Part From Middleware"), 0, [](const char *msg, RtData &d) {
403 Master *m = (Master*)d.obj;
404 Part *p = *(Part**)rtosc_argument(msg, 1).b.data;
405 int i = rtosc_argument(msg, 0).i;
406 m->part[i]->cloneTraits(*p);
407 m->part[i]->kill_rt();
408 d.reply("/free", "sb", "Part", sizeof(void*), &m->part[i]);
409 m->part[i] = p;
410 p->initialize_rt();
411 for(int i=0; i<128; ++i)
412 m->activeNotes[i] = 0;
414 {"active_keys:", rProp("Obtain a list of active notes"), 0,
415 rBegin;
416 char keys[129] = {0};
417 for(int i=0; i<128; ++i)
418 keys[i] = m->activeNotes[i] ? 'T' : 'F';
419 d.broadcast(d.loc, keys);
420 rEnd},
421 {"Pvolume::i", rShort("volume") rProp(parameter) rLinear(0,127)
422 rDefault(80) rDoc("Master Volume"), 0,
423 [](const char *m, rtosc::RtData &d) {
424 if(rtosc_narguments(m)==0) {
425 d.reply(d.loc, "i", ((Master*)d.obj)->Pvolume);
426 } else if(rtosc_narguments(m)==1 && rtosc_type(m,0)=='i') {
427 ((Master*)d.obj)->setPvolume(limit<char>(rtosc_argument(m,0).i,0,127));
428 d.broadcast(d.loc, "i", ((Master*)d.obj)->Pvolume);}}},
429 {"volume::i", rShort("volume") rProp(parameter) rLinear(0,127)
430 rDefault(80) rDoc("Master Volume"), 0,
431 [](const char *m, rtosc::RtData &d) {
432 if(rtosc_narguments(m)==0) {
433 d.reply(d.loc, "i", ((Master*)d.obj)->Pvolume);
434 } else if(rtosc_narguments(m)==1 && rtosc_type(m,0)=='i') {
435 ((Master*)d.obj)->setPvolume(limit<char>(rtosc_argument(m,0).i,0,127));
436 d.broadcast(d.loc, "i", ((Master*)d.obj)->Pvolume);}}},
437 {"Psysefxvol#" STRINGIFY(NUM_SYS_EFX) "/::i", 0, &sysefxPort,
438 [](const char *msg, rtosc::RtData &d) {
439 SNIP;
440 sysefxPort.dispatch(msg, d);
442 {"sysefxfrom#" STRINGIFY(NUM_SYS_EFX) "/", rDoc("Routing Between System Effects"), &sysefsendto,
443 [](const char *msg, RtData&d) {
444 SNIP;
445 sysefsendto.dispatch(msg, d);
448 {"noteOn:iii", rDoc("Noteon Event"), 0,
449 [](const char *m,RtData &d){
450 Master *M = (Master*)d.obj;
451 M->noteOn(rtosc_argument(m,0).i,rtosc_argument(m,1).i,rtosc_argument(m,2).i);}},
453 {"noteOff:ii", rDoc("Noteoff Event"), 0,
454 [](const char *m,RtData &d){
455 Master *M = (Master*)d.obj;
456 M->noteOff(rtosc_argument(m,0).i,rtosc_argument(m,1).i);}},
457 {"virtual_midi_cc:iii", rDoc("MIDI CC Event"), 0,
458 [](const char *m,RtData &d){
459 Master *M = (Master*)d.obj;
460 M->setController(rtosc_argument(m,0).i,rtosc_argument(m,1).i,rtosc_argument(m,2).i);}},
462 {"setController:iii", rDoc("MIDI CC Event"), 0,
463 [](const char *m,RtData &d){
464 Master *M = (Master*)d.obj;
465 M->setController(rtosc_argument(m,0).i,rtosc_argument(m,1).i,rtosc_argument(m,2).i);}},
466 {"Panic:", rDoc("Stop all sound"), 0,
467 [](const char *, RtData &d) {
468 Master &M = *(Master*)d.obj;
469 M.ShutUp();
471 {"freeze_state:", rProp(internal) rDoc("Disable OSC event handling\n"
472 "This sets up a read-only mode from which it's safe for another"
473 " thread to save parameters"), 0,
474 [](const char *,RtData &d) {
475 Master *M = (Master*)d.obj;
476 std::atomic_thread_fence(std::memory_order_release);
477 M->frozenState = true;
478 d.reply("/state_frozen", "");}},
479 {"thaw_state:", rProp(internal) rDoc("Resume handling OSC messages\n"
480 "See /freeze_state for more information"), 0,
481 [](const char *,RtData &d) {
482 Master *M = (Master*)d.obj;
483 M->frozenState = false;}},
484 {"automate/", rDoc("MIDI Learn/Plugin Automation support"), &automate_ports,
485 [](const char *msg, RtData &d) {
486 SNIP;
487 d.obj = (void*)&((Master*)d.obj)->automate;
488 automate_ports.dispatch(msg, d);
490 {"close-ui:", rDoc("Request to close any connection named \"GUI\""), 0,
491 [](const char *, RtData &d) {
492 d.reply("/close-ui", "");}},
493 {"add-rt-memory:bi", rProp(internal) rDoc("Add Additional Memory To RT MemPool"), 0,
494 [](const char *msg, RtData &d)
496 Master &m = *(Master*)d.obj;
497 char *mem = *(char**)rtosc_argument(msg, 0).b.data;
498 int i = rtosc_argument(msg, 1).i;
499 m.memory->addMemory(mem, i);
500 m.pendingMemory = false;
502 {"samplerate:", rMap(unit, Hz) rDoc("Get synthesizer sample rate"), 0, [](const char *, RtData &d) {
503 Master &m = *(Master*)d.obj;
504 d.reply("/samplerate", "f", m.synth.samplerate_f);
506 {"oscilsize:", rDoc("Get synthesizer oscillator size"), 0, [](const char *, RtData &d) {
507 Master &m = *(Master*)d.obj;
508 d.reply("/oscilsize", "f", m.synth.oscilsize_f);
509 d.reply("/oscilsize", "i", m.synth.oscilsize);
511 {"undo_pause:",rProp(internal) rDoc("pause undo event recording"),0,
512 [](const char *, rtosc::RtData &d) {d.reply("/undo_pause", "");}},
513 {"undo_resume:",rProp(internal) rDoc("resume undo event recording"),0,
514 [](const char *, rtosc::RtData &d) {d.reply("/undo_resume", "");}},
515 {"config/", rNoWalk rDoc("Top Level Application Configuration Parameters"),
516 &Config::ports, [](const char *, rtosc::RtData &d){d.forward();}},
517 {"presets/", rDoc("Parameter Presets"), &preset_ports, rBOIL_BEGIN
518 SNIP
519 preset_ports.dispatch(msg, data);
520 rBOIL_END},
521 {"HDDRecorder/preparefile:s", rDoc("Init WAV file"), 0, [](const char *msg, RtData &d) {
522 Master *m = (Master*)d.obj;
523 m->HDDRecorder.preparefile(rtosc_argument(msg, 0).s, 1);}},
524 {"HDDRecorder/start:", rDoc("Start recording"), 0, [](const char *, RtData &d) {
525 Master *m = (Master*)d.obj;
526 m->HDDRecorder.start();}},
527 {"HDDRecorder/stop:", rDoc("Stop recording"), 0, [](const char *, RtData &d) {
528 Master *m = (Master*)d.obj;
529 m->HDDRecorder.stop();}},
530 {"HDDRecorder/pause:", rDoc("Pause recording"), 0, [](const char *, RtData &d) {
531 Master *m = (Master*)d.obj;
532 m->HDDRecorder.pause();}},
533 {"watch/", rDoc("Interface to grab out live synthesis state"), &watchPorts,
534 rBOIL_BEGIN;
535 SNIP;
536 watchPorts.dispatch(msg, data);
537 rBOIL_END},
538 {"bank/", rDoc("Controls for instrument banks"), &bankPorts,
539 [](const char*,RtData&) {}},
540 {"learn:s", rProp(depricated) rDoc("MIDI Learn"), 0,
541 rBegin;
542 int free_slot = m->automate.free_slot();
543 if(free_slot >= 0) {
544 m->automate.createBinding(free_slot, rtosc_argument(msg, 0).s, true);
545 m->automate.active_slot = free_slot;
547 rEnd},
550 #undef rBegin
551 #undef rEnd
553 const Ports &Master::ports = master_ports;
555 class DataObj:public rtosc::RtData
557 public:
558 DataObj(char *loc_, size_t loc_size_, void *obj_, rtosc::ThreadLink *bToU_)
560 memset(loc_, 0, loc_size_);
561 loc = loc_;
562 loc_size = loc_size_;
563 obj = obj_;
564 bToU = bToU_;
565 forwarded = false;
568 virtual void replyArray(const char *path, const char *args, rtosc_arg_t *vals) override
570 char *buffer = bToU->buffer();
571 rtosc_amessage(buffer,bToU->buffer_size(),path,args,vals);
572 reply(buffer);
574 virtual void reply(const char *path, const char *args, ...) override
576 va_list va;
577 va_start(va,args);
578 char *buffer = bToU->buffer();
579 rtosc_vmessage(buffer,bToU->buffer_size(),path,args,va);
580 reply(buffer);
581 va_end(va);
583 virtual void reply(const char *msg) override
585 if(rtosc_message_length(msg, -1) == 0)
586 fprintf(stderr, "Warning: Invalid Rtosc message '%s'\n", msg);
587 bToU->raw_write(msg);
589 virtual void broadcast(const char *path, const char *args, ...) override{
590 va_list va;
591 va_start(va,args);
592 reply("/broadcast", "");
593 char *buffer = bToU->buffer();
594 rtosc_vmessage(buffer,bToU->buffer_size(),path,args,va);
595 reply(buffer);
596 va_end(va);
598 virtual void broadcast(const char *msg) override
600 reply("/broadcast", "");
601 reply(msg);
604 virtual void forward(const char *reason) override
606 assert(message);
607 reply("/forward", "");
608 printf("forwarding '%s'\n", message);
609 forwarded = true;
611 bool forwarded;
612 private:
613 rtosc::ThreadLink *bToU;
616 vuData::vuData(void)
617 :outpeakl(0.0f), outpeakr(0.0f), maxoutpeakl(0.0f), maxoutpeakr(0.0f),
618 rmspeakl(0.0f), rmspeakr(0.0f), clipped(0)
621 void Master::saveAutomation(XMLwrapper &xml, const rtosc::AutomationMgr &midi)
623 xml.beginbranch("automation");
625 XmlNode metadata("mgr-info");
626 metadata["nslots"] = to_s(midi.nslots);
627 metadata["nautomations"] = to_s(midi.per_slot);
628 metadata["ncontrol"] = to_s(midi.slots[0].automations[0].map.npoints);
629 xml.add(metadata);
631 for(int i=0; i<midi.nslots; ++i) {
632 const auto &slot = midi.slots[i];
633 if(!slot.used)
634 continue;
635 xml.beginbranch("slot", i);
636 XmlNode params("params");
637 params["midi-cc"] = to_s(slot.midi_cc);
638 xml.add(params);
639 for(int j=0; j<midi.per_slot; ++j) {
640 const auto &au = slot.automations[j];
641 if(!au.used)
642 continue;
643 xml.beginbranch("automation", j);
644 XmlNode automation("params");
645 automation["path"] = au.param_path;
646 XmlNode mapping("mapping");
647 mapping["gain"] = to_s(au.map.gain);
648 mapping["offset"] = to_s(au.map.offset);
649 xml.add(automation);
650 xml.add(mapping);
651 xml.endbranch();
654 xml.endbranch();
657 xml.endbranch();
660 void Master::loadAutomation(XMLwrapper &xml, rtosc::AutomationMgr &midi)
662 if(xml.enterbranch("automation")) {
663 for(int i=0; i<midi.nslots; ++i) {
664 auto &slot = midi.slots[i];
665 if(xml.enterbranch("slot", i)) {
666 for(int j=0; j<midi.per_slot; ++j) {
667 auto &au = slot.automations[j];
668 if(xml.enterbranch("automation", j)) {
669 float gain = 1.0;
670 float offset = 0.0;
671 std::string path = "";
672 for(auto node:xml.getBranch()) {
673 if(node.name == "params")
674 path = node["path"];
675 else if(node.name == "mapping") {
676 gain = atof(node["gain"].c_str());
677 offset = atof(node["offset"].c_str());
680 printf("createBinding(%d, %s, false)\n", i, path.c_str());
681 midi.createBinding(i, path.c_str(), false);
682 midi.setSlotSubGain(i, j, gain);
683 midi.setSlotSubOffset(i, j, offset);
684 xml.exitbranch();
687 for(auto node:xml.getBranch())
688 if(node.name == "params")
689 slot.midi_cc = atoi(node["midi-cc"].c_str());
690 xml.exitbranch();
693 xml.exitbranch();
697 Master::Master(const SYNTH_T &synth_, Config* config)
698 :HDDRecorder(synth_), time(synth_), ctl(synth_, &time),
699 microtonal(config->cfg.GzipCompression), bank(config),
700 automate(16,4,8),
701 frozenState(false), pendingMemory(false),
702 synth(synth_), gzip_compression(config->cfg.GzipCompression)
704 bToU = NULL;
705 uToB = NULL;
707 //Setup MIDI Learn
708 automate.set_ports(master_ports);
709 automate.set_instance(this);
710 //midi.frontend = [this](const char *msg) {bToU->raw_write(msg);};
711 automate.backend = [this](const char *msg) {applyOscEvent(msg);};
713 memory = new AllocatorClass();
714 swaplr = 0;
715 off = 0;
716 smps = 0;
717 bufl = new float[synth.buffersize];
718 bufr = new float[synth.buffersize];
720 last_xmz[0] = 0;
721 fft = new FFTwrapper(synth.oscilsize);
723 shutup = 0;
724 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
725 vuoutpeakpart[npart] = 1e-9;
726 fakepeakpart[npart] = 0;
729 ScratchString ss;
730 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
731 part[npart] = new Part(*memory, synth, time, config->cfg.GzipCompression,
732 config->cfg.Interpolation, &microtonal, fft, &watcher,
733 (ss+"/part"+npart+"/").c_str);
735 //Insertion Effects init
736 for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
737 insefx[nefx] = new EffectMgr(*memory, synth, 1, &time);
739 //System Effects init
740 for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx)
741 sysefx[nefx] = new EffectMgr(*memory, synth, 0, &time);
743 //Note Visualization
744 for(int i=0; i<128; ++i)
745 activeNotes[i] = 0;
747 defaults();
749 mastercb = 0;
750 mastercb_ptr = 0;
753 void Master::applyOscEvent(const char *msg)
755 char loc_buf[1024];
756 DataObj d{loc_buf, 1024, this, bToU};
757 memset(loc_buf, 0, sizeof(loc_buf));
758 d.matches = 0;
760 if(strcmp(msg, "/get-vu") && false) {
761 fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 5 + 30, 0 + 40);
762 fprintf(stdout, "backend[*]: '%s'<%s>\n", msg,
763 rtosc_argument_string(msg));
764 fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
767 ports.dispatch(msg, d, true);
768 if(d.matches == 0 && !d.forwarded)
769 fprintf(stderr, "Unknown path '%s:%s'\n", msg, rtosc_argument_string(msg));
770 if(d.forwarded)
771 bToU->raw_write(msg);
774 void Master::defaults()
776 volume = 1.0f;
777 setPvolume(80);
778 setPkeyshift(64);
780 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
781 part[npart]->defaults();
782 part[npart]->partno = npart % NUM_MIDI_CHANNELS;
783 part[npart]->Prcvchn = npart % NUM_MIDI_CHANNELS;
786 partonoff(0, 1); //enable the first part
788 for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) {
789 insefx[nefx]->defaults();
790 Pinsparts[nefx] = -1;
793 //System Effects init
794 for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) {
795 sysefx[nefx]->defaults();
796 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
797 setPsysefxvol(npart, nefx, 0);
799 for(int nefxto = 0; nefxto < NUM_SYS_EFX; ++nefxto)
800 setPsysefxsend(nefx, nefxto, 0);
803 microtonal.defaults();
804 ShutUp();
808 * Note On Messages (velocity=0 for NoteOff)
810 void Master::noteOn(char chan, char note, char velocity)
812 if(velocity) {
813 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
814 if(chan == part[npart]->Prcvchn) {
815 fakepeakpart[npart] = velocity * 2;
816 if(part[npart]->Penabled)
817 part[npart]->NoteOn(note, velocity, keyshift);
820 activeNotes[(int)note] = 1;
822 else
823 this->noteOff(chan, note);
824 HDDRecorder.triggernow();
828 * Note Off Messages
830 void Master::noteOff(char chan, char note)
832 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
833 if((chan == part[npart]->Prcvchn) && part[npart]->Penabled)
834 part[npart]->NoteOff(note);
835 activeNotes[(int)note] = 0;
839 * Pressure Messages (velocity=0 for NoteOff)
841 void Master::polyphonicAftertouch(char chan, char note, char velocity)
843 if(velocity) {
844 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
845 if(chan == part[npart]->Prcvchn)
846 if(part[npart]->Penabled)
847 part[npart]->PolyphonicAftertouch(note, velocity, keyshift);
850 else
851 this->noteOff(chan, note);
855 * Controllers
857 void Master::setController(char chan, int type, int par)
859 if(frozenState)
860 return;
861 automate.handleMidi(chan, type, par);
862 if((type == C_dataentryhi) || (type == C_dataentrylo)
863 || (type == C_nrpnhi) || (type == C_nrpnlo)) { //Process RPN and NRPN by the Master (ignore the chan)
864 ctl.setparameternumber(type, par);
866 int parhi = -1, parlo = -1, valhi = -1, vallo = -1;
867 if(ctl.getnrpn(&parhi, &parlo, &valhi, &vallo) == 0) { //this is NRPN
868 switch(parhi) {
869 case 0x04: //System Effects
870 if(parlo < NUM_SYS_EFX)
871 sysefx[parlo]->seteffectparrt(valhi, vallo);
872 break;
873 case 0x08: //Insertion Effects
874 if(parlo < NUM_INS_EFX)
875 insefx[parlo]->seteffectparrt(valhi, vallo);
876 break;
879 } else { //other controllers
880 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) //Send the controller to all part assigned to the channel
881 if((chan == part[npart]->Prcvchn) && (part[npart]->Penabled != 0))
882 part[npart]->SetController(type, par);
884 if(type == C_allsoundsoff) { //cleanup insertion/system FX
885 for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx)
886 sysefx[nefx]->cleanup();
887 for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
888 insefx[nefx]->cleanup();
893 void Master::vuUpdate(const float *outl, const float *outr)
895 //Peak computation (for vumeters)
896 vu.outpeakl = 1e-12;
897 vu.outpeakr = 1e-12;
898 for(int i = 0; i < synth.buffersize; ++i) {
899 if(fabs(outl[i]) > vu.outpeakl)
900 vu.outpeakl = fabs(outl[i]);
901 if(fabs(outr[i]) > vu.outpeakr)
902 vu.outpeakr = fabs(outr[i]);
904 if((vu.outpeakl > 1.0f) || (vu.outpeakr > 1.0f))
905 vu.clipped = 1;
906 if(vu.maxoutpeakl < vu.outpeakl)
907 vu.maxoutpeakl = vu.outpeakl;
908 if(vu.maxoutpeakr < vu.outpeakr)
909 vu.maxoutpeakr = vu.outpeakr;
911 //RMS Peak computation (for vumeters)
912 vu.rmspeakl = 1e-12;
913 vu.rmspeakr = 1e-12;
914 for(int i = 0; i < synth.buffersize; ++i) {
915 vu.rmspeakl += outl[i] * outl[i];
916 vu.rmspeakr += outr[i] * outr[i];
918 vu.rmspeakl = sqrt(vu.rmspeakl / synth.buffersize_f);
919 vu.rmspeakr = sqrt(vu.rmspeakr / synth.buffersize_f);
921 //Part Peak computation (for Part vumeters or fake part vumeters)
922 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
923 vuoutpeakpart[npart] = 1.0e-12f;
924 if(part[npart]->Penabled != 0) {
925 float *outl = part[npart]->partoutl,
926 *outr = part[npart]->partoutr;
927 for(int i = 0; i < synth.buffersize; ++i) {
928 float tmp = fabs(outl[i] + outr[i]);
929 if(tmp > vuoutpeakpart[npart])
930 vuoutpeakpart[npart] = tmp;
932 vuoutpeakpart[npart] *= volume;
934 else
935 if(fakepeakpart[npart] > 1)
936 fakepeakpart[npart]--;
941 * Enable/Disable a part
943 void Master::partonoff(int npart, int what)
945 if(npart >= NUM_MIDI_PARTS)
946 return;
947 if(what == 0) { //disable part
948 fakepeakpart[npart] = 0;
949 part[npart]->Penabled = 0;
950 part[npart]->cleanup();
951 for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) {
952 if(Pinsparts[nefx] == npart)
953 insefx[nefx]->cleanup();
956 else { //enabled
957 part[npart]->Penabled = 1;
958 fakepeakpart[npart] = 0;
962 void Master::setMasterChangedCallback(void(*cb)(void*,Master*), void *ptr)
964 mastercb = cb;
965 mastercb_ptr = ptr;
968 #if 0
969 template <class T>
970 struct def_skip
972 static void skip(const char*& argptr) { argptr += sizeof(T); }
975 template <class T>
976 struct str_skip
978 static void skip(const char*& argptr) { while(argptr++); /*TODO: 4 padding */ }
981 template<class T, class Display = T, template<class TMP> class SkipsizeFunc = def_skip>
982 void _dump_prim_arg(const char*& argptr, std::ostream& os)
984 os << ' ' << (Display)*(const T*)argptr;
985 SkipsizeFunc<T>::skip(argptr);
988 void dump_msg(const char* ptr, std::ostream& os = std::cerr)
990 assert(*ptr == '/');
991 os << ptr;
993 while(*++ptr) ; // skip address
994 while(!*++ptr) ; // skip 0s
996 assert(*ptr == ',');
997 os << ' ' << (ptr + 1);
999 const char* argptr = ptr;
1000 while(*++argptr) ; // skip type string
1001 while(!*++argptr) ; // skip 0s
1003 char c;
1004 while((c = *++ptr))
1006 switch(c)
1008 case 'i':
1009 _dump_prim_arg<int32_t>(argptr, os); break;
1010 case 'c':
1011 _dump_prim_arg<int32_t, char>(argptr, os); break;
1012 // case 's':
1013 // _dump_prim_arg<char, const char*>(argptr, os); break;
1014 default:
1015 exit(1);
1020 #endif
1021 int msg_id=0;
1023 bool Master::runOSC(float *outl, float *outr, bool offline)
1025 //Handle user events TODO move me to a proper location
1026 char loc_buf[1024];
1027 DataObj d{loc_buf, 1024, this, bToU};
1028 memset(loc_buf, 0, sizeof(loc_buf));
1029 int events = 0;
1030 while(uToB && uToB->hasNext() && events < 100) {
1031 const char *msg = uToB->read();
1033 if(!strcmp(msg, "/load-master")) {
1034 Master *this_master = this;
1035 Master *new_master = *(Master**)rtosc_argument(msg, 0).b.data;
1036 if(!offline)
1037 new_master->AudioOut(outl, outr);
1038 Nio::masterSwap(new_master);
1039 if (mastercb)
1040 mastercb(mastercb_ptr, new_master);
1041 bToU->write("/free", "sb", "Master", sizeof(Master*), &this_master);
1042 return false;
1045 //XXX yes, this is not realtime safe, but it is useful...
1046 if(strcmp(msg, "/get-vu") && false) {
1047 fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 5 + 30, 0 + 40);
1048 fprintf(stdout, "backend[%d]: '%s'<%s>\n", msg_id++, msg,
1049 rtosc_argument_string(msg));
1050 fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
1052 ports.dispatch(msg, d, true);
1053 events++;
1054 if(!d.matches) {
1055 //workaround for requesting voice status
1056 int a=0, b=0, c=0;
1057 char e=0;
1058 if(4 == sscanf(msg, "/part%d/kit%d/adpars/VoicePar%d/Enable%c", &a, &b, &c, &e)) {
1059 d.reply(msg, "F");
1060 d.matches++;
1063 if(!d.matches) {// && !ports.apropos(msg)) {
1064 fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 1, 7 + 30, 0 + 40);
1065 fprintf(stderr, "Unknown address<BACKEND:%s> '%s:%s'\n",
1066 offline ? "offline" : "online",
1067 uToB->peak(),
1068 rtosc_argument_string(uToB->peak()));
1069 fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
1073 if(automate.damaged) {
1074 d.broadcast("/damage", "s", "/automate/");
1075 automate.damaged = 0;
1078 if(events>1 && false)
1079 fprintf(stderr, "backend: %d events per cycle\n",events);
1081 return true;
1085 * Master audio out (the final sound)
1087 bool Master::AudioOut(float *outr, float *outl)
1089 //Danger Limits
1090 if(memory->lowMemory(2,1024*1024))
1091 printf("QUITE LOW MEMORY IN THE RT POOL BE PREPARED FOR WEIRD BEHAVIOR!!\n");
1092 //Normal Limits
1093 if(!pendingMemory && memory->lowMemory(4,1024*1024)) {
1094 printf("Requesting more memory\n");
1095 bToU->write("/request-memory", "");
1096 pendingMemory = true;
1099 //work through events
1100 if(!runOSC(outl, outr, false))
1101 return false;
1104 //Handle watch points
1105 if(bToU)
1106 watcher.write_back = bToU;
1107 watcher.tick();
1110 //Swaps the Left channel with Right Channel
1111 if(swaplr)
1112 swap(outl, outr);
1114 //clean up the output samples (should not be needed?)
1115 memset(outl, 0, synth.bufferbytes);
1116 memset(outr, 0, synth.bufferbytes);
1118 //Compute part samples and store them part[npart]->partoutl,partoutr
1119 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
1120 if(part[npart]->Penabled)
1121 part[npart]->ComputePartSmps();
1123 //Insertion effects
1124 for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
1125 if(Pinsparts[nefx] >= 0) {
1126 int efxpart = Pinsparts[nefx];
1127 if(part[efxpart]->Penabled)
1128 insefx[nefx]->out(part[efxpart]->partoutl,
1129 part[efxpart]->partoutr);
1133 //Apply the part volumes and pannings (after insertion effects)
1134 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
1135 if(!part[npart]->Penabled)
1136 continue;
1138 Stereo<float> newvol(part[npart]->volume),
1139 oldvol(part[npart]->oldvolumel,
1140 part[npart]->oldvolumer);
1142 float pan = part[npart]->panning;
1143 if(pan < 0.5f)
1144 newvol.l *= pan * 2.0f;
1145 else
1146 newvol.r *= (1.0f - pan) * 2.0f;
1147 //if(npart==0)
1148 //printf("[%d]vol = %f->%f\n", npart, oldvol.l, newvol.l);
1150 //the volume or the panning has changed and needs interpolation
1151 if(ABOVE_AMPLITUDE_THRESHOLD(oldvol.l, newvol.l)
1152 || ABOVE_AMPLITUDE_THRESHOLD(oldvol.r, newvol.r)) {
1153 for(int i = 0; i < synth.buffersize; ++i) {
1154 Stereo<float> vol(INTERPOLATE_AMPLITUDE(oldvol.l, newvol.l,
1155 i, synth.buffersize),
1156 INTERPOLATE_AMPLITUDE(oldvol.r, newvol.r,
1157 i, synth.buffersize));
1158 part[npart]->partoutl[i] *= vol.l;
1159 part[npart]->partoutr[i] *= vol.r;
1161 part[npart]->oldvolumel = newvol.l;
1162 part[npart]->oldvolumer = newvol.r;
1164 else {
1165 for(int i = 0; i < synth.buffersize; ++i) { //the volume did not changed
1166 part[npart]->partoutl[i] *= newvol.l;
1167 part[npart]->partoutr[i] *= newvol.r;
1173 //System effects
1174 for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) {
1175 if(sysefx[nefx]->geteffect() == 0)
1176 continue; //the effect is disabled
1178 float tmpmixl[synth.buffersize];
1179 float tmpmixr[synth.buffersize];
1180 //Clean up the samples used by the system effects
1181 memset(tmpmixl, 0, synth.bufferbytes);
1182 memset(tmpmixr, 0, synth.bufferbytes);
1184 //Mix the channels according to the part settings about System Effect
1185 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
1186 //skip if the part has no output to effect
1187 if(Psysefxvol[nefx][npart] == 0)
1188 continue;
1190 //skip if the part is disabled
1191 if(part[npart]->Penabled == 0)
1192 continue;
1194 //the output volume of each part to system effect
1195 const float vol = sysefxvol[nefx][npart];
1196 for(int i = 0; i < synth.buffersize; ++i) {
1197 tmpmixl[i] += part[npart]->partoutl[i] * vol;
1198 tmpmixr[i] += part[npart]->partoutr[i] * vol;
1202 // system effect send to next ones
1203 for(int nefxfrom = 0; nefxfrom < nefx; ++nefxfrom)
1204 if(Psysefxsend[nefxfrom][nefx] != 0) {
1205 const float vol = sysefxsend[nefxfrom][nefx];
1206 for(int i = 0; i < synth.buffersize; ++i) {
1207 tmpmixl[i] += sysefx[nefxfrom]->efxoutl[i] * vol;
1208 tmpmixr[i] += sysefx[nefxfrom]->efxoutr[i] * vol;
1212 sysefx[nefx]->out(tmpmixl, tmpmixr);
1214 //Add the System Effect to sound output
1215 const float outvol = sysefx[nefx]->sysefxgetvolume();
1216 for(int i = 0; i < synth.buffersize; ++i) {
1217 outl[i] += tmpmixl[i] * outvol;
1218 outr[i] += tmpmixr[i] * outvol;
1222 //Mix all parts
1223 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
1224 if(part[npart]->Penabled) //only mix active parts
1225 for(int i = 0; i < synth.buffersize; ++i) { //the volume did not changed
1226 outl[i] += part[npart]->partoutl[i];
1227 outr[i] += part[npart]->partoutr[i];
1230 //Insertion effects for Master Out
1231 for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
1232 if(Pinsparts[nefx] == -2)
1233 insefx[nefx]->out(outl, outr);
1236 //Master Volume
1237 for(int i = 0; i < synth.buffersize; ++i) {
1238 outl[i] *= volume;
1239 outr[i] *= volume;
1242 vuUpdate(outl, outr);
1244 //Shutup if it is asked (with fade-out)
1245 if(shutup) {
1246 for(int i = 0; i < synth.buffersize; ++i) {
1247 float tmp = (synth.buffersize_f - i) / synth.buffersize_f;
1248 outl[i] *= tmp;
1249 outr[i] *= tmp;
1251 ShutUp();
1254 //update the global frame timer
1255 time++;
1257 #ifdef DEMO_VERSION
1258 double seconds = time.time()*synth.buffersize_f/synth.samplerate_f;
1259 if(seconds > 10*60) {//10 minute trial
1260 shutup = true;
1261 for(int i = 0; i < synth.buffersize; ++i) {
1262 float tmp = (synth.buffersize_f - i) / synth.buffersize_f;
1263 outl[i] *= 0.0f;
1264 outr[i] *= 0.0f;
1267 #endif
1269 //Update pulse
1270 last_ack = last_beat;
1273 return true;
1276 //TODO review the respective code from yoshimi for this
1277 //If memory serves correctly, libsamplerate was used
1278 void Master::GetAudioOutSamples(size_t nsamples,
1279 unsigned samplerate,
1280 float *outl,
1281 float *outr)
1283 off_t out_off = 0;
1285 //Fail when resampling rather than doing a poor job
1286 if(synth.samplerate != samplerate) {
1287 printf("darn it: %d vs %d\n", synth.samplerate, samplerate);
1288 return;
1291 while(nsamples) {
1292 //use all available samples
1293 if(nsamples >= smps) {
1294 memcpy(outl + out_off, bufl + off, sizeof(float) * smps);
1295 memcpy(outr + out_off, bufr + off, sizeof(float) * smps);
1296 nsamples -= smps;
1298 //generate samples
1299 if (! AudioOut(bufl, bufr))
1300 return;
1302 off = 0;
1303 out_off += smps;
1304 smps = synth.buffersize;
1306 else { //use some samples
1307 memcpy(outl + out_off, bufl + off, sizeof(float) * nsamples);
1308 memcpy(outr + out_off, bufr + off, sizeof(float) * nsamples);
1309 smps -= nsamples;
1310 off += nsamples;
1311 nsamples = 0;
1316 Master::~Master()
1318 delete []bufl;
1319 delete []bufr;
1321 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
1322 delete part[npart];
1323 for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
1324 delete insefx[nefx];
1325 for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx)
1326 delete sysefx[nefx];
1328 delete fft;
1329 delete memory;
1334 * Parameter control
1336 void Master::setPvolume(char Pvolume_)
1338 Pvolume = Pvolume_;
1339 volume = dB2rap((Pvolume - 96.0f) / 96.0f * 40.0f);
1342 void Master::setPkeyshift(char Pkeyshift_)
1344 Pkeyshift = Pkeyshift_;
1345 keyshift = (int)Pkeyshift - 64;
1349 void Master::setPsysefxvol(int Ppart, int Pefx, char Pvol)
1351 Psysefxvol[Pefx][Ppart] = Pvol;
1352 sysefxvol[Pefx][Ppart] = powf(0.1f, (1.0f - Pvol / 96.0f) * 2.0f);
1355 void Master::setPsysefxsend(int Pefxfrom, int Pefxto, char Pvol)
1357 Psysefxsend[Pefxfrom][Pefxto] = Pvol;
1358 sysefxsend[Pefxfrom][Pefxto] = powf(0.1f, (1.0f - Pvol / 96.0f) * 2.0f);
1363 * Panic! (Clean up all parts and effects)
1365 void Master::ShutUp()
1367 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
1368 part[npart]->cleanup();
1369 fakepeakpart[npart] = 0;
1371 for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
1372 insefx[nefx]->cleanup();
1373 for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx)
1374 sysefx[nefx]->cleanup();
1375 for(int i = 0; i < int(sizeof(activeNotes)/sizeof(activeNotes[0])); ++i)
1376 activeNotes[i] = 0;
1377 vuresetpeaks();
1378 shutup = 0;
1383 * Reset peaks and clear the "cliped" flag (for VU-meter)
1385 void Master::vuresetpeaks()
1387 vu.outpeakl = 1e-9;
1388 vu.outpeakr = 1e-9;
1389 vu.maxoutpeakl = 1e-9;
1390 vu.maxoutpeakr = 1e-9;
1391 vu.clipped = 0;
1394 void Master::applyparameters(void)
1396 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
1397 part[npart]->applyparameters();
1400 void Master::initialize_rt(void)
1402 for(int i=0; i<NUM_SYS_EFX; ++i)
1403 sysefx[i]->init();
1404 for(int i=0; i<NUM_INS_EFX; ++i)
1405 insefx[i]->init();
1407 for(int i=0; i<NUM_MIDI_PARTS; ++i)
1408 part[i]->initialize_rt();
1411 void Master::add2XML(XMLwrapper& xml)
1413 xml.addpar("volume", Pvolume);
1414 xml.addpar("key_shift", Pkeyshift);
1415 xml.addparbool("nrpn_receive", ctl.NRPN.receive);
1417 xml.beginbranch("MICROTONAL");
1418 microtonal.add2XML(xml);
1419 xml.endbranch();
1421 saveAutomation(xml, automate);
1423 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
1424 xml.beginbranch("PART", npart);
1425 part[npart]->add2XML(xml);
1426 xml.endbranch();
1429 xml.beginbranch("SYSTEM_EFFECTS");
1430 for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) {
1431 xml.beginbranch("SYSTEM_EFFECT", nefx);
1432 xml.beginbranch("EFFECT");
1433 sysefx[nefx]->add2XML(xml);
1434 xml.endbranch();
1436 for(int pefx = 0; pefx < NUM_MIDI_PARTS; ++pefx) {
1437 xml.beginbranch("VOLUME", pefx);
1438 xml.addpar("vol", Psysefxvol[nefx][pefx]);
1439 xml.endbranch();
1442 for(int tonefx = nefx + 1; tonefx < NUM_SYS_EFX; ++tonefx) {
1443 xml.beginbranch("SENDTO", tonefx);
1444 xml.addpar("send_vol", Psysefxsend[nefx][tonefx]);
1445 xml.endbranch();
1449 xml.endbranch();
1451 xml.endbranch();
1453 xml.beginbranch("INSERTION_EFFECTS");
1454 for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) {
1455 xml.beginbranch("INSERTION_EFFECT", nefx);
1456 xml.addpar("part", Pinsparts[nefx]);
1458 xml.beginbranch("EFFECT");
1459 insefx[nefx]->add2XML(xml);
1460 xml.endbranch();
1461 xml.endbranch();
1464 xml.endbranch();
1468 int Master::getalldata(char **data)
1470 XMLwrapper xml;
1472 xml.beginbranch("MASTER");
1474 add2XML(xml);
1476 xml.endbranch();
1478 *data = xml.getXMLdata();
1479 return strlen(*data) + 1;
1482 void Master::putalldata(const char *data)
1484 XMLwrapper xml;
1485 if(!xml.putXMLdata(data)) {
1486 return;
1489 if(xml.enterbranch("MASTER") == 0)
1490 return;
1492 getfromXML(xml);
1494 xml.exitbranch();
1497 int Master::saveXML(const char *filename)
1499 XMLwrapper xml;
1501 xml.beginbranch("MASTER");
1502 add2XML(xml);
1503 xml.endbranch();
1505 return xml.saveXMLfile(filename, gzip_compression);
1509 int Master::loadXML(const char *filename)
1511 XMLwrapper xml;
1513 if(xml.loadXMLfile(filename) < 0) {
1514 return -1;
1517 if(xml.enterbranch("MASTER") == 0)
1518 return -10;
1520 getfromXML(xml);
1521 xml.exitbranch();
1523 initialize_rt();
1524 return 0;
1527 void Master::getfromXML(XMLwrapper& xml)
1529 setPvolume(xml.getpar127("volume", Pvolume));
1530 setPkeyshift(xml.getpar127("key_shift", Pkeyshift));
1531 ctl.NRPN.receive = xml.getparbool("nrpn_receive", ctl.NRPN.receive);
1534 part[0]->Penabled = 0;
1535 for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
1536 if(xml.enterbranch("PART", npart) == 0)
1537 continue;
1538 part[npart]->getfromXML(xml);
1539 xml.exitbranch();
1542 if(xml.enterbranch("MICROTONAL")) {
1543 microtonal.getfromXML(xml);
1544 xml.exitbranch();
1547 loadAutomation(xml, automate);
1549 sysefx[0]->changeeffect(0);
1550 if(xml.enterbranch("SYSTEM_EFFECTS")) {
1551 for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) {
1552 if(xml.enterbranch("SYSTEM_EFFECT", nefx) == 0)
1553 continue;
1554 if(xml.enterbranch("EFFECT")) {
1555 sysefx[nefx]->getfromXML(xml);
1556 xml.exitbranch();
1559 for(int partefx = 0; partefx < NUM_MIDI_PARTS; ++partefx) {
1560 if(xml.enterbranch("VOLUME", partefx) == 0)
1561 continue;
1562 setPsysefxvol(partefx, nefx,
1563 xml.getpar127("vol", Psysefxvol[partefx][nefx]));
1564 xml.exitbranch();
1567 for(int tonefx = nefx + 1; tonefx < NUM_SYS_EFX; ++tonefx) {
1568 if(xml.enterbranch("SENDTO", tonefx) == 0)
1569 continue;
1570 setPsysefxsend(nefx, tonefx,
1571 xml.getpar127("send_vol",
1572 Psysefxsend[nefx][tonefx]));
1573 xml.exitbranch();
1575 xml.exitbranch();
1577 xml.exitbranch();
1581 if(xml.enterbranch("INSERTION_EFFECTS")) {
1582 for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) {
1583 if(xml.enterbranch("INSERTION_EFFECT", nefx) == 0)
1584 continue;
1585 Pinsparts[nefx] = xml.getpar("part",
1586 Pinsparts[nefx],
1588 NUM_MIDI_PARTS);
1589 if(xml.enterbranch("EFFECT")) {
1590 insefx[nefx]->getfromXML(xml);
1591 xml.exitbranch();
1593 xml.exitbranch();
1596 xml.exitbranch();
1600 static rtosc_version version_in_rtosc_fmt()
1602 return rtosc_version
1604 (unsigned char) version.get_major(),
1605 (unsigned char) version.get_minor(),
1606 (unsigned char) version.get_revision()
1610 char* Master::getXMLData()
1612 XMLwrapper xml;
1614 xml.beginbranch("MASTER");
1615 add2XML(xml);
1616 xml.endbranch();
1618 return xml.getXMLdata();
1621 int Master::saveOSC(const char *filename)
1623 std::string savefile = rtosc::save_to_file(ports, this,
1624 "ZynAddSubFX",
1625 version_in_rtosc_fmt());
1627 zyn::Config config;
1628 zyn::SYNTH_T* synth = new zyn::SYNTH_T;
1629 synth->buffersize = 256;
1630 synth->samplerate = 48000;
1631 synth->alias();
1633 zyn::Master master2(*synth, &config);
1634 int rval = master2.loadOSCFromStr(savefile.c_str());
1637 if(rval < 0)
1639 std::cerr << "invalid savefile!" << std::endl;
1640 std::cerr << "complete savefile:" << std::endl;
1641 std::cerr << savefile << std::endl;
1642 std::cerr << "first entry that could not be parsed:" << std::endl;
1644 for(int i = -rval + 1; savefile[i]; ++i)
1645 if(savefile[i] == '\n')
1647 savefile.resize(i);
1648 break;
1650 std::cerr << (savefile.c_str() - rval) << std::endl;
1652 rval = -1;
1654 else
1656 char* xml = getXMLData(),
1657 * xml2 = master2.getXMLData();
1659 rval = strcmp(xml, xml2) ? -1 : 0;
1661 if(rval == 0)
1663 if(filename)
1665 std::ofstream ofs(filename);
1666 ofs << savefile;
1668 else if(!filename)
1669 std::cout << savefile << std::endl;
1671 else
1673 std::cout << savefile << std::endl;
1674 std::cerr << "Can not write OSC savefile!! (see tmp1.txt and tmp2.txt)"
1675 << std::endl;
1676 std::ofstream tmp1("tmp1.txt"), tmp2("tmp2.txt");
1677 tmp1 << xml;
1678 tmp2 << xml2;
1681 free(xml);
1682 free(xml2);
1684 return rval;
1687 int Master::loadOSCFromStr(const char *filename)
1689 return rtosc::load_from_file(filename,
1690 ports, this,
1691 "ZynAddSubFX", version_in_rtosc_fmt());
1694 string loadfile(string fname)
1696 std::ifstream t(fname.c_str());
1697 std::string str((std::istreambuf_iterator<char>(t)),
1698 std::istreambuf_iterator<char>());
1699 return str;
1702 int Master::loadOSC(const char *filename)
1704 int rval = loadOSCFromStr(loadfile(filename).c_str());
1705 return rval < 0 ? rval : 0;