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
)
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
);
73 //Now get the second index like normal
74 while(!isdigit(*m
)) 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
]);
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
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
))
111 master
.setPsysefxsend(ind1
, ind2
, rtosc_argument(m
,0).i
);
113 d
.reply(d
.loc
, "i", master
.Psysefxsend
[ind1
][ind2
]);
117 #define rBegin [](const char *msg, RtData &d) { rtosc::AutomationMgr &a = *(AutomationMgr*)d.obj
120 static int extract_num(const char *&msg
)
122 while(*msg
&& !isdigit(*msg
)) msg
++;
124 while(isdigit(*msg
)) msg
++;
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,
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
));
143 d
.reply(d
.loc
, "f", a
.getSlotSubOffset(slot
, param
));
145 {"gain::f", rProp(parameter
) rDefault(100) rShort("gain") rLinear(-200, 200) rMap(unit
, percent
), 0,
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
));
154 d
.reply(d
.loc
, "f", a
.getSlotSubGain(slot
, param
));
158 static const Ports auto_param_ports
= {
159 {"used::T:F", rProp(parameter
) rProp(read
-only
) rDoc("If automation is assigned to anything"), 0,
162 int param
= d
.idx
[0];
164 d
.reply(d
.loc
, a
.slots
[slot
].automations
[param
].used
? "T" : "F");
166 {"active::T:F", rProp(parameter
) rDoc("If automation is being actively used"), 0,
169 int param
= d
.idx
[0];
170 if(rtosc_narguments(msg
))
171 a
.slots
[slot
].automations
[param
].active
= rtosc_argument(msg
, 0).T
;
173 d
.reply(d
.loc
, a
.slots
[slot
].automations
[param
].active
? "T" : "F");
175 {"path::s", rProp(parameter
) rProp(read
-only
) rDoc("Path of parameter"), 0,
178 int param
= d
.idx
[0];
179 d
.reply(d
.loc
, "s", a
.slots
[slot
].automations
[param
].param_path
);
181 {"clear:", rDoc("Clear automation param"), 0,
184 int param
= d
.idx
[0];
185 a
.clearSlotSub(slot
, param
);
187 {"mapping/", 0, &mapping_ports
,
190 mapping_ports
.dispatch(msg
, d
);
193 //{"mapping", rDoc("Parameter mapping control"), 0,
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;
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);
208 static const Ports slot_ports
= {
209 //{"learn-binding:s", rDoc("Create binding for automation path with midi-learn"), 0,
212 // //m->automate.createBinding(rtosc_argument(msg, 0).i,
213 // // rtosc_argument(msg, 1).s,
214 // // rtosc_argument(msg, 2).T);
216 //{"create-binding:s", rDoc("Create binding for automation path"), 0,
218 // m->automate.createBinding(rtosc_argument(msg, 0).i,
219 // rtosc_argument(msg, 1).s,
220 // rtosc_argument(msg, 2).T);
222 {"value::f", rProp(parameter
) rMap(default, 0.5) rLinear(0, 1) rDoc("Access current value in slot 'i' (0..1)"), 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
));
229 d
.reply(d
.loc
, "f", a
.getSlot(num
));
232 {"name::s", rProp(parameter
) rDoc("Access name of automation slot"), 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
));
239 d
.reply(d
.loc
, "s", a
.getName(num
));
241 {"midi-cc::i", rProp(parameter
) rMap(default, -1) rDoc("Access assigned midi CC slot") , 0,
244 if(rtosc_narguments(msg
))
245 a
.slots
[slot
].midi_cc
= rtosc_argument(msg
, 0).i
;
247 d
.reply(d
.loc
, "i", a
.slots
[slot
].midi_cc
);
250 {"active::T:F", rProp(parameter
) rMap(default, F
) rDoc("If Slot is enabled"), 0,
253 if(rtosc_narguments(msg
))
254 a
.slots
[slot
].active
= rtosc_argument(msg
, 0).T
;
256 d
.reply(d
.loc
, a
.slots
[slot
].active
? "T" : "F");
258 {"learning::i", rProp(parameter
) rMap(default, -1) rDoc("If slot is trying to find a midi learn binding"), 0,
261 d
.reply(d
.loc
, "i", a
.slots
[slot
].learning
);
263 {"clear:", rDoc("Clear automation slot"), 0,
268 {"param#4/", rDoc("Info on individual param mappings"), &auto_param_ports
,
271 d
.push_index(get_next_int(msg
));
273 auto_param_ports
.dispatch(msg
, d
);
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,
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
);
285 d
.reply(d
.loc
, "i", a
.active_slot
);
287 {"learn-binding-new-slot:s", rDoc("Learn a parameter assigned to a new slot"), 0,
289 int free_slot
= a
.free_slot();
291 a
.createBinding(free_slot
, rtosc_argument(msg
, 0).s
, true);
292 a
.active_slot
= free_slot
;
295 {"learn-binding-same-slot:s", rDoc("Learn a parameter appending to the active-slot"), 0,
297 if(a
.active_slot
>= 0)
298 a
.createBinding(a
.active_slot
, rtosc_argument(msg
, 0).s
, true);
300 // TODO: remove rNoWalk
301 {"slot#16/", rNoWalk
rDoc("Parameters of individual automation slots"), &slot_ports
,
304 d
.push_index(get_next_int(msg
));
306 slot_ports
.dispatch(msg
, d
);
309 {"clear", rDoc("Clear all automation slots"), 0,
311 for(int i
=0; i
<a
.nslots
; ++i
)
314 {"load-blob:b", rProp(internal
) rDoc("Load blob from middleware"), 0,
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
]);
347 #define rBegin [](const char *msg, RtData &d) { Master *m = (Master*)d.obj
350 static const Ports watchPorts
= {
351 {"add:s", rDoc("Add synthesis state to watch"), 0,
353 m
->watcher
.add_watch(rtosc_argument(msg
,0).s
);
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
) {
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
)
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
]);
411 for(int i
=0; i
<128; ++i
)
412 m
->activeNotes
[i
] = 0;
414 {"active_keys:", rProp("Obtain a list of active notes"), 0,
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
);
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
) {
440 sysefxPort
.dispatch(msg
, d
);
442 {"sysefxfrom#" STRINGIFY(NUM_SYS_EFX
) "/", rDoc("Routing Between System Effects"), &sysefsendto
,
443 [](const char *msg
, RtData
&d
) {
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
;
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
) {
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
519 preset_ports
.dispatch(msg
, data
);
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
,
536 watchPorts
.dispatch(msg
, data
);
538 {"bank/", rDoc("Controls for instrument banks"), &bankPorts
,
539 [](const char*,RtData
&) {}},
540 {"learn:s", rProp(depricated
) rDoc("MIDI Learn"), 0,
542 int free_slot
= m
->automate
.free_slot();
544 m
->automate
.createBinding(free_slot
, rtosc_argument(msg
, 0).s
, true);
545 m
->automate
.active_slot
= free_slot
;
553 const Ports
&Master::ports
= master_ports
;
555 class DataObj
:public rtosc::RtData
558 DataObj(char *loc_
, size_t loc_size_
, void *obj_
, rtosc::ThreadLink
*bToU_
)
560 memset(loc_
, 0, loc_size_
);
562 loc_size
= loc_size_
;
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
);
574 virtual void reply(const char *path
, const char *args
, ...) override
578 char *buffer
= bToU
->buffer();
579 rtosc_vmessage(buffer
,bToU
->buffer_size(),path
,args
,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
{
592 reply("/broadcast", "");
593 char *buffer
= bToU
->buffer();
594 rtosc_vmessage(buffer
,bToU
->buffer_size(),path
,args
,va
);
598 virtual void broadcast(const char *msg
) override
600 reply("/broadcast", "");
604 virtual void forward(const char *reason
) override
607 reply("/forward", "");
608 printf("forwarding '%s'\n", message
);
613 rtosc::ThreadLink
*bToU
;
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
);
631 for(int i
=0; i
<midi
.nslots
; ++i
) {
632 const auto &slot
= midi
.slots
[i
];
635 xml
.beginbranch("slot", i
);
636 XmlNode
params("params");
637 params
["midi-cc"] = to_s(slot
.midi_cc
);
639 for(int j
=0; j
<midi
.per_slot
; ++j
) {
640 const auto &au
= slot
.automations
[j
];
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
);
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
)) {
671 std::string path
= "";
672 for(auto node
:xml
.getBranch()) {
673 if(node
.name
== "params")
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
);
687 for(auto node
:xml
.getBranch())
688 if(node
.name
== "params")
689 slot
.midi_cc
= atoi(node
["midi-cc"].c_str());
697 Master::Master(const SYNTH_T
&synth_
, Config
* config
)
698 :HDDRecorder(synth_
), time(synth_
), ctl(synth_
, &time
),
699 microtonal(config
->cfg
.GzipCompression
), bank(config
),
701 frozenState(false), pendingMemory(false),
702 synth(synth_
), gzip_compression(config
->cfg
.GzipCompression
)
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();
717 bufl
= new float[synth
.buffersize
];
718 bufr
= new float[synth
.buffersize
];
721 fft
= new FFTwrapper(synth
.oscilsize
);
724 for(int npart
= 0; npart
< NUM_MIDI_PARTS
; ++npart
) {
725 vuoutpeakpart
[npart
] = 1e-9;
726 fakepeakpart
[npart
] = 0;
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
, µtonal
, 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
);
744 for(int i
=0; i
<128; ++i
)
753 void Master::applyOscEvent(const char *msg
)
756 DataObj d
{loc_buf
, 1024, this, bToU
};
757 memset(loc_buf
, 0, sizeof(loc_buf
));
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
));
771 bToU
->raw_write(msg
);
774 void Master::defaults()
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();
808 * Note On Messages (velocity=0 for NoteOff)
810 void Master::noteOn(char chan
, char note
, char 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;
823 this->noteOff(chan
, note
);
824 HDDRecorder
.triggernow();
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
)
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
);
851 this->noteOff(chan
, note
);
857 void Master::setController(char chan
, int type
, int par
)
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
869 case 0x04: //System Effects
870 if(parlo
< NUM_SYS_EFX
)
871 sysefx
[parlo
]->seteffectparrt(valhi
, vallo
);
873 case 0x08: //Insertion Effects
874 if(parlo
< NUM_INS_EFX
)
875 insefx
[parlo
]->seteffectparrt(valhi
, vallo
);
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)
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
))
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)
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
;
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
)
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();
957 part
[npart
]->Penabled
= 1;
958 fakepeakpart
[npart
] = 0;
962 void Master::setMasterChangedCallback(void(*cb
)(void*,Master
*), void *ptr
)
972 static void skip(const char*& argptr
) { argptr
+= sizeof(T
); }
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
)
993 while(*++ptr
) ; // skip address
994 while(!*++ptr
) ; // skip 0s
997 os
<< ' ' << (ptr
+ 1);
999 const char* argptr
= ptr
;
1000 while(*++argptr
) ; // skip type string
1001 while(!*++argptr
) ; // skip 0s
1009 _dump_prim_arg
<int32_t>(argptr
, os
); break;
1011 _dump_prim_arg
<int32_t, char>(argptr
, os
); break;
1013 // _dump_prim_arg<char, const char*>(argptr, os); break;
1023 bool Master::runOSC(float *outl
, float *outr
, bool offline
)
1025 //Handle user events TODO move me to a proper location
1027 DataObj d
{loc_buf
, 1024, this, bToU
};
1028 memset(loc_buf
, 0, sizeof(loc_buf
));
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
;
1037 new_master
->AudioOut(outl
, outr
);
1038 Nio::masterSwap(new_master
);
1040 mastercb(mastercb_ptr
, new_master
);
1041 bToU
->write("/free", "sb", "Master", sizeof(Master
*), &this_master
);
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);
1055 //workaround for requesting voice status
1058 if(4 == sscanf(msg
, "/part%d/kit%d/adpars/VoicePar%d/Enable%c", &a
, &b
, &c
, &e
)) {
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",
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
);
1085 * Master audio out (the final sound)
1087 bool Master::AudioOut(float *outr
, float *outl
)
1090 if(memory
->lowMemory(2,1024*1024))
1091 printf("QUITE LOW MEMORY IN THE RT POOL BE PREPARED FOR WEIRD BEHAVIOR!!\n");
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))
1104 //Handle watch points
1106 watcher
.write_back
= bToU
;
1110 //Swaps the Left channel with Right Channel
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();
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
)
1138 Stereo
<float> newvol(part
[npart
]->volume
),
1139 oldvol(part
[npart
]->oldvolumel
,
1140 part
[npart
]->oldvolumer
);
1142 float pan
= part
[npart
]->panning
;
1144 newvol
.l
*= pan
* 2.0f
;
1146 newvol
.r
*= (1.0f
- pan
) * 2.0f
;
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
;
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
;
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)
1190 //skip if the part is disabled
1191 if(part
[npart
]->Penabled
== 0)
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
;
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
);
1237 for(int i
= 0; i
< synth
.buffersize
; ++i
) {
1242 vuUpdate(outl
, outr
);
1244 //Shutup if it is asked (with fade-out)
1246 for(int i
= 0; i
< synth
.buffersize
; ++i
) {
1247 float tmp
= (synth
.buffersize_f
- i
) / synth
.buffersize_f
;
1254 //update the global frame timer
1258 double seconds
= time
.time()*synth
.buffersize_f
/synth
.samplerate_f
;
1259 if(seconds
> 10*60) {//10 minute trial
1261 for(int i
= 0; i
< synth
.buffersize
; ++i
) {
1262 float tmp
= (synth
.buffersize_f
- i
) / synth
.buffersize_f
;
1270 last_ack
= last_beat
;
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
,
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
);
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
);
1299 if (! AudioOut(bufl
, bufr
))
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
);
1321 for(int npart
= 0; npart
< NUM_MIDI_PARTS
; ++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
];
1336 void Master::setPvolume(char 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
)
1383 * Reset peaks and clear the "cliped" flag (for VU-meter)
1385 void Master::vuresetpeaks()
1389 vu
.maxoutpeakl
= 1e-9;
1390 vu
.maxoutpeakr
= 1e-9;
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
)
1404 for(int i
=0; i
<NUM_INS_EFX
; ++i
)
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
);
1421 saveAutomation(xml
, automate
);
1423 for(int npart
= 0; npart
< NUM_MIDI_PARTS
; ++npart
) {
1424 xml
.beginbranch("PART", npart
);
1425 part
[npart
]->add2XML(xml
);
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
);
1436 for(int pefx
= 0; pefx
< NUM_MIDI_PARTS
; ++pefx
) {
1437 xml
.beginbranch("VOLUME", pefx
);
1438 xml
.addpar("vol", Psysefxvol
[nefx
][pefx
]);
1442 for(int tonefx
= nefx
+ 1; tonefx
< NUM_SYS_EFX
; ++tonefx
) {
1443 xml
.beginbranch("SENDTO", tonefx
);
1444 xml
.addpar("send_vol", Psysefxsend
[nefx
][tonefx
]);
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
);
1468 int Master::getalldata(char **data
)
1472 xml
.beginbranch("MASTER");
1478 *data
= xml
.getXMLdata();
1479 return strlen(*data
) + 1;
1482 void Master::putalldata(const char *data
)
1485 if(!xml
.putXMLdata(data
)) {
1489 if(xml
.enterbranch("MASTER") == 0)
1497 int Master::saveXML(const char *filename
)
1501 xml
.beginbranch("MASTER");
1505 return xml
.saveXMLfile(filename
, gzip_compression
);
1509 int Master::loadXML(const char *filename
)
1513 if(xml
.loadXMLfile(filename
) < 0) {
1517 if(xml
.enterbranch("MASTER") == 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)
1538 part
[npart
]->getfromXML(xml
);
1542 if(xml
.enterbranch("MICROTONAL")) {
1543 microtonal
.getfromXML(xml
);
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)
1554 if(xml
.enterbranch("EFFECT")) {
1555 sysefx
[nefx
]->getfromXML(xml
);
1559 for(int partefx
= 0; partefx
< NUM_MIDI_PARTS
; ++partefx
) {
1560 if(xml
.enterbranch("VOLUME", partefx
) == 0)
1562 setPsysefxvol(partefx
, nefx
,
1563 xml
.getpar127("vol", Psysefxvol
[partefx
][nefx
]));
1567 for(int tonefx
= nefx
+ 1; tonefx
< NUM_SYS_EFX
; ++tonefx
) {
1568 if(xml
.enterbranch("SENDTO", tonefx
) == 0)
1570 setPsysefxsend(nefx
, tonefx
,
1571 xml
.getpar127("send_vol",
1572 Psysefxsend
[nefx
][tonefx
]));
1581 if(xml
.enterbranch("INSERTION_EFFECTS")) {
1582 for(int nefx
= 0; nefx
< NUM_INS_EFX
; ++nefx
) {
1583 if(xml
.enterbranch("INSERTION_EFFECT", nefx
) == 0)
1585 Pinsparts
[nefx
] = xml
.getpar("part",
1589 if(xml
.enterbranch("EFFECT")) {
1590 insefx
[nefx
]->getfromXML(xml
);
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()
1614 xml
.beginbranch("MASTER");
1618 return xml
.getXMLdata();
1621 int Master::saveOSC(const char *filename
)
1623 std::string savefile
= rtosc::save_to_file(ports
, this,
1625 version_in_rtosc_fmt());
1628 zyn::SYNTH_T
* synth
= new zyn::SYNTH_T
;
1629 synth
->buffersize
= 256;
1630 synth
->samplerate
= 48000;
1633 zyn::Master
master2(*synth
, &config
);
1634 int rval
= master2
.loadOSCFromStr(savefile
.c_str());
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')
1650 std::cerr
<< (savefile
.c_str() - rval
) << std::endl
;
1656 char* xml
= getXMLData(),
1657 * xml2
= master2
.getXMLData();
1659 rval
= strcmp(xml
, xml2
) ? -1 : 0;
1665 std::ofstream
ofs(filename
);
1669 std::cout
<< savefile
<< std::endl
;
1673 std::cout
<< savefile
<< std::endl
;
1674 std::cerr
<< "Can not write OSC savefile!! (see tmp1.txt and tmp2.txt)"
1676 std::ofstream
tmp1("tmp1.txt"), tmp2("tmp2.txt");
1687 int Master::loadOSCFromStr(const char *filename
)
1689 return rtosc::load_from_file(filename
,
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>());
1702 int Master::loadOSC(const char *filename
)
1704 int rval
= loadOSCFromStr(loadfile(filename
).c_str());
1705 return rval
< 0 ? rval
: 0;