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.
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>
35 #include <sys/types.h>
44 using namespace rtosc
;
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
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]))
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
]);
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
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]))
103 int ind1
= atoi(index_1
);
105 //Now get the second index like normal
106 while(!isdigit(*m
)) 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
]);
116 d
.reply(d
.loc
, "i", master
.Psysefxsend
[ind1
][ind2
]);
120 #define rBegin [](const char *msg, RtData &d) { \
122 rtosc::AutomationMgr &a = *(AutomationMgr*)d.obj
125 static int extract_num(const char *&msg
)
127 while(*msg
&& !isdigit(*msg
)) msg
++;
129 while(isdigit(*msg
)) msg
++;
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,
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
));
148 d
.reply(d
.loc
, "f", a
.getSlotSubOffset(slot
, param
));
150 {"gain::f", rProp(parameter
) rDefault(100) rShort("gain") rLinear(-200, 200) rMap(unit
, percent
), 0,
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
));
159 d
.reply(d
.loc
, "f", a
.getSlotSubGain(slot
, param
));
163 static const Ports auto_param_ports
= {
164 {"used::T:F", rProp(parameter
) rProp(read
-only
) rDoc("If automation is assigned to anything"), 0,
167 int param
= d
.idx
[0];
169 d
.reply(d
.loc
, a
.slots
[slot
].automations
[param
].used
? "T" : "F");
171 {"active::T:F", rProp(parameter
) rDoc("If automation is being actively used"), 0,
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");
180 d
.reply(d
.loc
, a
.slots
[slot
].automations
[param
].active
? "T" : "F");
182 {"path::s", rProp(parameter
) rDoc("Path of parameter"), 0,
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
);
192 d
.reply(d
.loc
, "s", a
.slots
[slot
].automations
[param
].param_path
);
194 {"clear:", rDoc("Clear automation param"), 0,
197 int param
= d
.idx
[0];
198 a
.clearSlotSub(slot
, param
);
200 {"mapping/", 0, &mapping_ports
,
204 mapping_ports
.dispatch(msg
, d
);
207 //{"mapping", rDoc("Parameter mapping control"), 0,
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;
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);
222 static const Ports slot_ports
= {
223 //{"learn-binding:s", rDoc("Create binding for automation path with midi-learn"), 0,
226 // //m->automate.createBinding(rtosc_argument(msg, 0).i,
227 // // rtosc_argument(msg, 1).s,
228 // // rtosc_argument(msg, 2).T);
230 //{"create-binding:s", rDoc("Create binding for automation path"), 0,
232 // m->automate.createBinding(rtosc_argument(msg, 0).i,
233 // rtosc_argument(msg, 1).s,
234 // rtosc_argument(msg, 2).T);
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,
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
));
243 d
.reply(d
.loc
, "f", a
.getSlot(num
));
246 {"name::s", rProp(parameter
) rDoc("Access name of automation slot"), 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
));
253 d
.reply(d
.loc
, "s", a
.getName(num
));
255 {"midi-cc::i", rProp(parameter
) rMap(default, -1) rDoc("Access assigned midi CC slot") , 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
);
262 d
.reply(d
.loc
, "i", a
.slots
[slot
].midi_cc
);
265 {"midi-nrpn::i", rProp(parameter
) rMap(default, -1) rDoc("Access assigned midi NRPN slot") , 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
);
272 d
.reply(d
.loc
, "i", a
.slots
[slot
].midi_nrpn
);
275 {"active::T:F", rProp(parameter
) rMap(default, F
) rDoc("If Slot is enabled"), 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");
283 d
.reply(d
.loc
, a
.slots
[slot
].active
? "T" : "F");
285 {"learning::i", rProp(parameter
) rMap(default, -1) rDoc("If slot is trying to find a midi learn binding"), 0,
288 d
.reply(d
.loc
, "i", a
.slots
[slot
].learning
);
290 {"clear:", rDoc("Clear automation slot"), 0,
295 {"param#4/", rDoc("Info on individual param mappings"), &auto_param_ports
,
298 d
.push_index(get_next_int(msg
));
300 auto_param_ports
.dispatch(msg
, d
);
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,
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
);
312 d
.reply(d
.loc
, "i", a
.active_slot
);
314 {"learn-binding-new-slot:s", rDoc("Learn a parameter assigned to a new slot"), 0,
316 int free_slot
= a
.free_slot();
318 a
.createBinding(free_slot
, rtosc_argument(msg
, 0).s
, true);
319 a
.active_slot
= free_slot
;
322 {"learn-binding-same-slot:s", rDoc("Learn a parameter appending to the active-slot"), 0,
324 if(a
.active_slot
>= 0)
325 a
.createBinding(a
.active_slot
, rtosc_argument(msg
, 0).s
, true);
327 {"slot#16/", rDoc("Parameters of individual automation slots"), &slot_ports
,
330 d
.push_index(get_next_int(msg
));
332 slot_ports
.dispatch(msg
, d
);
335 {"clear", rDoc("Clear all automation slots"), 0,
337 for(int i
=0; i
<a
.nslots
; ++i
)
340 {"load-blob:b", rProp(internal
) rDoc("Load blob from middleware"), 0,
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
);
377 #define rBegin [](const char *msg, RtData &d) { (void)msg; Master *m = (Master*)d.obj
380 static const Ports watchPorts
= {
381 {"add:s", rDoc("Add synthesis state to watch"), 0,
383 if(!m
->watcher
.active(rtosc_argument(msg
,0).s
))
384 m
->watcher
.add_watch(rtosc_argument(msg
,0).s
);
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
) {
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
)
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
]);
444 memset(m
->activeNotes
, 0, sizeof(m
->activeNotes
));
446 {"active_keys:", rProp("Obtain a list of active notes"), 0,
449 for(int i
=0; i
<128; ++i
)
450 keys
[i
] = m
->activeNotes
[i
] ? 'T' : 'F';
451 d
.broadcast(d
.loc
, keys
);
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));
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));
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
) {
477 sysefxPort
.dispatch(msg
, d
);
479 {"sysefxfrom#" STRINGIFY(NUM_SYS_EFX
) "/", rDoc("Routing Between System Effects"), &sysefsendto
,
480 [](const char *msg
, RtData
&d
) {
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
);
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,
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
);
513 d
.reply(d
.loc
, "i", m
->time
.tempo
);
515 {"Panic:", rDoc("Stop all sound"), 0,
516 [](const char *, RtData
&d
) {
517 Master
&M
= *(Master
*)d
.obj
;
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
;
537 printf("residue message = <%s>\n", msg
);
539 rtosc::MidiMapperRT::ports
.dispatch(msg
,d
);}},
540 {"automate/", rDoc("MIDI Learn/Plugin Automation support"), &automate_ports
,
541 [](const char *msg
, RtData
&d
) {
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,
573 if(!strcmp("", args
)) {
574 data
.reply(loc
, "c", obj
->dnd_buffer
);
575 *obj
->dnd_buffer
= 0;
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);
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
588 preset_ports
.dispatch(msg
, data
);
590 {"watch/", rDoc("Interface to grab out live synthesis state"), &watchPorts
,
593 watchPorts
.dispatch(msg
, data
);
595 {"bank/", rDoc("Controls for instrument banks"), &bankPorts
,
596 [](const char*,RtData
&) {}},
597 {"learn:s", rProp(deprecated
) rDoc("MIDI Learn"), 0,
599 int free_slot
= m
->automate
.free_slot();
601 m
->automate
.createBinding(free_slot
, rtosc_argument(msg
, 0).s
, true);
602 m
->automate
.active_slot
= free_slot
;
610 const Ports
&Master::ports
= master_ports
;
612 class DataObj
:public rtosc::RtData
615 DataObj(char *loc_
, size_t loc_size_
, void *obj_
, rtosc::ThreadLink
*bToU_
)
617 memset(loc_
, 0, loc_size_
);
619 loc_size
= loc_size_
;
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
);
631 virtual void reply(const char *path
, const char *args
, ...) override
635 char *buffer
= bToU
->buffer();
636 rtosc_vmessage(buffer
,bToU
->buffer_size(),path
,args
,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
{
649 reply("/broadcast", "");
650 char *buffer
= bToU
->buffer();
651 rtosc_vmessage(buffer
,bToU
->buffer_size(),path
,args
,va
);
655 virtual void broadcast(const char *msg
) override
657 reply("/broadcast", "");
661 virtual void forward(const char *reason
) override
665 reply("/forward", "");
666 printf("forwarding '%s'\n", message
);
671 rtosc::ThreadLink
*bToU
;
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
);
689 for(int i
=0; i
<midi
.nslots
; ++i
) {
690 const auto &slot
= midi
.slots
[i
];
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
);
698 for(int j
=0; j
<midi
.per_slot
; ++j
) {
699 const auto &au
= slot
.automations
[j
];
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
);
719 void Master::loadAutomation(XMLwrapper
&xml
, rtosc::AutomationMgr
&midi
)
722 for(int i
=0; i
<midi
.nslots
; ++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
)) {
733 std::string path
= "";
734 for(auto node
:xml
.getBranch()) {
735 if(node
.name
== "params")
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
);
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);
768 Master::Master(const SYNTH_T
&synth_
, Config
* config
)
769 :HDDRecorder(synth_
), time(synth_
), ctl(synth_
, &time
),
770 microtonal(config
->cfg
.GzipCompression
), bank(config
),
772 frozenState(false), pendingMemory(false),
773 synth(synth_
), gzip_compression(config
->cfg
.GzipCompression
)
775 SaveFullXml
=(config
->cfg
.SaveFullXml
==1);
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();
793 bufl
= new float[synth
.buffersize
];
794 bufr
= new float[synth
.buffersize
];
797 fft
= new FFTwrapper(synth
.oscilsize
);
800 for(int npart
= 0; npart
< NUM_MIDI_PARTS
; ++npart
) {
801 vuoutpeakpartl
[npart
] = 1e-9;
802 vuoutpeakpartr
[npart
] = 1e-9;
803 fakepeakpart
[npart
] = 0;
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
, µtonal
, 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
);
831 memset(activeNotes
, 0, sizeof(activeNotes
));
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"
854 new_master
->AudioOut(outl
, outr
);
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
);
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
;
868 mastercb(mastercb_ptr
, new_master
);
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);
876 fprintf(stdout
, "backend[%d]: '%s'<%s>\n", msg_id
, msg
,
877 rtosc_argument_string(msg
));
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);
887 //workaround for requesting voice status
890 if(4 == sscanf(msg
, "/part%d/kit%d/adpars/VoicePar%d/Enable%c", &a
, &b
, &c
, &e
)) {
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",
900 rtosc_argument_string(uToB
->peak()));
901 fprintf(stderr
, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
904 bToU
->raw_write(msg
);
909 bool Master::applyOscEvent(const char *msg
, float *outl
, float *outr
,
910 bool offline
, bool nio
, int msg_id
)
913 DataObj d
{loc_buf
, 1024, this, bToU
};
914 memset(loc_buf
, 0, sizeof(loc_buf
));
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;
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();
960 * Note On Messages (velocity=0 for NoteOff)
962 void Master::noteOn(char chan
, note_t note
, char velocity
, float note_log2_freq
)
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();
976 this->noteOff(chan
, note
);
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
);
1004 void Master::setController(char chan
, int type
, int par
)
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
1017 case 0x04: //System Effects
1018 if(parlo
< NUM_SYS_EFX
)
1019 sysefx
[parlo
]->seteffectparrt(valhi
, vallo
);
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
);
1028 midi
.handleCC(parhi
<<7&parlo
,valhi
<<7&vallo
, chan
, true);
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
)
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
))
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
]);
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
)
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();
1125 part
[npart
]->Penabled
= 1;
1126 fakepeakpart
[npart
] = 0;
1130 void Master::setMasterChangedCallback(void(*cb
)(void*,Master
*), void *ptr
)
1136 void Master::copyMasterCbTo(Master
*dest
)
1138 dest
->mastercb
= mastercb
;
1139 dest
->mastercb_ptr
= mastercb_ptr
;
1142 bool Master::hasMasterCb() const
1147 void Master::setAudioCompressor(bool enabled
)
1149 Nio::setAudioCompressor(enabled
);
1156 static void skip(const char*& argptr
) { argptr
+= sizeof(T
); }
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
== '/');
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
1193 _dump_prim_arg
<int32_t>(argptr
, os
); break;
1195 _dump_prim_arg
<int32_t, char>(argptr
, os
); break;
1197 // _dump_prim_arg<char, const char*>(argptr, os); break;
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
1220 DataObj d
{loc_buf
, 1024, this, bToU
};
1221 memset(loc_buf
, 0, sizeof(loc_buf
));
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
,
1230 run_osc_in_use
.store(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);
1246 else { return true; /* = no new master */ }
1250 * Master audio out (the final sound)
1252 bool Master::AudioOut(float *outl
, float *outr
)
1255 if(memory
->lowMemory(2,1024*1024))
1256 printf("QUITE LOW MEMORY IN THE RT POOL BE PREPARED FOR WEIRD BEHAVIOR!!\n");
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))
1269 //Handle watch points
1271 watcher
.write_back
= bToU
;
1275 //Swaps the Left channel with Right Channel
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();
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
)
1306 Stereo
<float> newvol(part
[npart
]->gain
);
1308 float pan
= part
[npart
]->panning
;
1310 newvol
.r
*= pan
* 2.0f
;
1312 newvol
.l
*= (1.0f
- pan
) * 2.0f
;
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
];
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
];
1337 for ( int i
= 0; i
< synth
.buffersize
; ++i
)
1338 part
[npart
]->partoutr
[i
] *= newvol
.r
;
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)
1359 //skip if the part is disabled
1360 if(part
[npart
]->Penabled
== 0)
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
;
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
);
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
];
1418 for ( int i
= 0; i
< synth
.buffersize
; ++i
)
1425 vuUpdate(outl
, outr
);
1427 //Shutup if it is asked (with fade-out)
1429 for(int i
= 0; i
< synth
.buffersize
; ++i
) {
1430 float tmp
= (synth
.buffersize_f
- i
) / synth
.buffersize_f
;
1437 //update the global frame timer
1441 double seconds
= time
.time()*synth
.buffersize_f
/synth
.samplerate_f
;
1442 if(seconds
> 10*60) {//10 minute trial
1444 for(int i
= 0; i
< synth
.buffersize
; ++i
) {
1452 last_ack
= last_beat
;
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
,
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
);
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
);
1481 if (! AudioOut(bufl
, bufr
))
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
);
1503 for(int npart
= 0; npart
< NUM_MIDI_PARTS
; ++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
];
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
));
1564 * Reset peaks and clear the "cliped" flag (for VU-meter)
1566 void Master::vuresetpeaks()
1570 vu
.maxoutpeakl
= 1e-9;
1571 vu
.maxoutpeakr
= 1e-9;
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
)
1585 for(int i
=0; i
<NUM_INS_EFX
; ++i
)
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
);
1603 xml
.SaveFullXml
=true; // save disabled parts
1607 saveAutomation(xml
, automate
);
1608 for(int npart
= 0; npart
< NUM_MIDI_PARTS
; ++npart
) {
1609 xml
.beginbranch("PART", npart
);
1610 part
[npart
]->add2XML(xml
);
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
);
1621 for(int pefx
= 0; pefx
< NUM_MIDI_PARTS
; ++pefx
) {
1622 xml
.beginbranch("VOLUME", pefx
);
1623 xml
.addpar("vol", Psysefxvol
[nefx
][pefx
]);
1627 for(int tonefx
= nefx
+ 1; tonefx
< NUM_SYS_EFX
; ++tonefx
) {
1628 xml
.beginbranch("SENDTO", tonefx
);
1629 xml
.addpar("send_vol", Psysefxsend
[nefx
][tonefx
]);
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
);
1653 int Master::getalldata(char **data
)
1657 xml
.beginbranch("MASTER");
1663 *data
= xml
.getXMLdata();
1664 return strlen(*data
) + 1;
1667 void Master::putalldata(const char *data
)
1670 if(!xml
.putXMLdata(data
)) {
1674 if(xml
.enterbranch("MASTER") == 0)
1682 int Master::saveXML(const char *filename
)
1686 xml
.beginbranch("MASTER");
1690 return xml
.saveXMLfile(filename
, gzip_compression
);
1694 int Master::loadXML(const char *filename
)
1698 if(xml
.loadXMLfile(filename
) < 0) {
1702 if(xml
.enterbranch("MASTER") == 0)
1712 void Master::getfromXML(XMLwrapper
& xml
)
1714 if (xml
.hasparreal("volume")) {
1715 Volume
= xml
.getparreal("volume", Volume
);
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)
1727 part
[npart
]->getfromXML(xml
);
1731 if(xml
.enterbranch("MICROTONAL")) {
1732 microtonal
.getfromXML(xml
);
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)
1743 if(xml
.enterbranch("EFFECT")) {
1744 sysefx
[nefx
]->getfromXML(xml
);
1748 for(int partefx
= 0; partefx
< NUM_MIDI_PARTS
; ++partefx
) {
1749 if(xml
.enterbranch("VOLUME", partefx
) == 0)
1751 setPsysefxvol(partefx
, nefx
,
1752 xml
.getpar127("vol", Psysefxvol
[partefx
][nefx
]));
1756 for(int tonefx
= nefx
+ 1; tonefx
< NUM_SYS_EFX
; ++tonefx
) {
1757 if(xml
.enterbranch("SENDTO", tonefx
) == 0)
1759 setPsysefxsend(nefx
, tonefx
,
1760 xml
.getpar127("send_vol",
1761 Psysefxsend
[nefx
][tonefx
]));
1770 if(xml
.enterbranch("INSERTION_EFFECTS")) {
1771 for(int nefx
= 0; nefx
< NUM_INS_EFX
; ++nefx
) {
1772 if(xml
.enterbranch("INSERTION_EFFECT", nefx
) == 0)
1774 Pinsparts
[nefx
] = xml
.getpar("part",
1778 if(xml
.enterbranch("EFFECT")) {
1779 insefx
[nefx
]->getfromXML(xml
);
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()
1803 xml
.beginbranch("MASTER");
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
1819 int Master::loadOSCFromStr(const char *file_content
,
1820 savefile_dispatcher_t
* dispatcher
)
1822 return rtosc::load_from_file(file_content
,
1824 "ZynAddSubFX", version_in_rtosc_fmt(),
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>());
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;