zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

Master.cpp (62801B)


      1 /*
      2   ZynAddSubFX - a software synthesizer
      3 
      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
      8 
      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.
     13 */
     14 
     15 #include "Master.h"
     16 
     17 #include "Part.h"
     18 
     19 #include "zyn-version.h"
     20 #include "../Misc/Stereo.h"
     21 #include "../Misc/Util.h"
     22 #include "../Misc/Sync.h"
     23 #include "../Params/LFOParams.h"
     24 #include "../Effects/EffectMgr.h"
     25 #include "../DSP/FFTwrapper.h"
     26 #include "../Misc/Allocator.h"
     27 #include "../Containers/ScratchString.h"
     28 #include "../Nio/Nio.h"
     29 #include "PresetExtractor.h"
     30 
     31 #include <rtosc/ports.h>
     32 #include <rtosc/port-sugar.h>
     33 #include <rtosc/thread-link.h>
     34 #include <stdio.h>
     35 #include <sys/stat.h>
     36 #include <sys/types.h>
     37 #include <fstream>
     38 #include <iostream>
     39 #include <algorithm>
     40 #include <cmath>
     41 #include <atomic>
     42 #include <unistd.h>
     43 
     44 using namespace std;
     45 using namespace rtosc;
     46 
     47 namespace zyn {
     48 
     49 #define rObject Master
     50 
     51 static const Ports sysefxPort =
     52 {
     53     {"part#" STRINGIFY(NUM_MIDI_PARTS) "::i", rProp(parameter) rDefault([0...])
     54         rDoc("gain on part to sysefx routing"), 0,
     55         [](const char *m, RtData&d)
     56         {
     57             //we know that if we are here the location must
     58             //be ...Psysefxvol#N/part#M
     59             //and the number "N" is one or two digits at most
     60 
     61             // go backto the '/'
     62             const char* m_findslash = m + strlen(m),
     63                       * loc_findslash = d.loc + strlen(d.loc);
     64             for(;*loc_findslash != '/'; --m_findslash, --loc_findslash)
     65                 assert(*loc_findslash == *m_findslash);
     66             assert(m_findslash + 1 == m);
     67 
     68             const char *index_1 = loc_findslash-1;
     69             assert(isdigit(*index_1));
     70             if(isdigit(index_1[-1]))
     71                 index_1--;
     72             int ind1 = atoi(index_1); //efx
     73 
     74             //Now get the second index like normal
     75             while(!isdigit(*m)) m++;
     76             int ind2 = atoi(m); //part
     77             Master &mast = *(Master*)d.obj;
     78 
     79             if(rtosc_narguments(m)) {
     80                 mast.setPsysefxvol(ind2, ind1, rtosc_argument(m,0).i /*vol*/);
     81                 d.broadcast(d.loc, "i", mast.Psysefxvol[ind1][ind2]);
     82             } else
     83                 d.reply(d.loc, "i", mast.Psysefxvol[ind1][ind2]);
     84         }}
     85 };
     86 
     87 static const Ports sysefsendto =
     88 {
     89     {"to#" STRINGIFY(NUM_SYS_EFX) "::i", rProp(parameter)  rDefault([0...])
     90         rDoc("sysefx to sysefx routing gain"), 0, [](const char *m, RtData&d)
     91         {
     92             //same workaround as before
     93             //go backto the '/'
     94             const char* m_findslash = m + strlen(m),
     95                       * loc_findslash = d.loc + strlen(d.loc);
     96             for(;*loc_findslash != '/'; --m_findslash, --loc_findslash)
     97                 assert(*loc_findslash == *m_findslash);
     98             assert(m_findslash + 1 == m);
     99 
    100             const char *index_1 = loc_findslash-1;
    101             assert(isdigit(*index_1));
    102             if(isdigit(index_1[-1]))
    103                 index_1--;
    104             int ind1 = atoi(index_1);
    105 
    106             //Now get the second index like normal
    107             while(!isdigit(*m)) m++;
    108             int ind2 = atoi(m);
    109             Master &master = *(Master*)d.obj;
    110 
    111             if(rtosc_narguments(m))
    112             {
    113                 master.setPsysefxsend(ind1, ind2, rtosc_argument(m,0).i);
    114                 d.broadcast(d.loc, "i", master.Psysefxsend[ind1][ind2]);
    115             }
    116             else
    117                 d.reply(d.loc, "i", master.Psysefxsend[ind1][ind2]);
    118         }}
    119 };
    120 
    121 #define rBegin [](const char *msg, RtData &d) { \
    122     (void) msg; \
    123     rtosc::AutomationMgr &a = *(AutomationMgr*)d.obj
    124 #define rEnd }
    125 
    126 static int extract_num(const char *&msg)
    127 {
    128     while(*msg && !isdigit(*msg)) msg++;
    129     int num = atoi(msg);
    130     while(isdigit(*msg)) msg++;
    131     return num;
    132 }
    133 
    134 static int get_next_int(const char *msg)
    135 {
    136     return extract_num(msg);
    137 }
    138 
    139 static const Ports mapping_ports = {
    140     {"offset::f", rProp(parameter) rDefault(0.0) rShort("off") rLinear(-50, 50) rMap(unit, percent), 0,
    141         rBegin;
    142         int slot = d.idx[1];
    143         int param = d.idx[0];
    144         if(!strcmp("f",rtosc_argument_string(msg))) {
    145             a.setSlotSubOffset(slot, param, rtosc_argument(msg, 0).f);
    146             a.updateMapping(slot, param);
    147             d.broadcast(d.loc, "f", a.getSlotSubOffset(slot, param));
    148         } else
    149             d.reply(d.loc, "f", a.getSlotSubOffset(slot, param));
    150         rEnd},
    151     {"gain::f", rProp(parameter) rDefault(100.0) rShort("gain") rLinear(-200, 200) rMap(unit, percent), 0,
    152         rBegin;
    153         int slot = d.idx[1];
    154         int param = d.idx[0];
    155         if(!strcmp("f",rtosc_argument_string(msg))) {
    156             a.setSlotSubGain(slot, param, rtosc_argument(msg, 0).f);
    157             a.updateMapping(slot, param);
    158             d.broadcast(d.loc, "f", a.getSlotSubGain(slot, param));
    159         } else
    160             d.reply(d.loc, "f", a.getSlotSubGain(slot, param));
    161         rEnd},
    162 };
    163 
    164 static const Ports auto_param_ports = {
    165     {"used::T:F", rProp(parameter) rProp(read-only) rDoc("If automation is assigned to anything"), 0,
    166         rBegin;
    167         int slot  = d.idx[1];
    168         int param = d.idx[0];
    169 
    170         d.reply(d.loc, a.slots[slot].automations[param].used ? "T" : "F");
    171         rEnd},
    172     {"active::T:F", rProp(parameter) rDoc("If automation is being actively used"), 0,
    173         rBegin;
    174         int slot  = d.idx[1];
    175         int param = d.idx[0];
    176         if(rtosc_narguments(msg)) {
    177             a.slots[slot].automations[param].active = rtosc_argument(msg, 0).T;
    178             d.broadcast(d.loc, a.slots[slot].automations[param].active ? "T" : "F");
    179         }
    180         else
    181             d.reply(d.loc, a.slots[slot].automations[param].active ? "T" : "F");
    182         rEnd},
    183     {"path::s", rProp(parameter) rDoc("Path of parameter"), 0,
    184         rBegin;
    185         int slot  = d.idx[1];
    186         int param = d.idx[0];
    187         if(!strcmp("s",rtosc_argument_string(msg))) {
    188             a.setSlotSubPath(slot, param, rtosc_argument(msg, 0).s);
    189             a.updateMapping(slot, param);
    190             d.broadcast(d.loc, "s", a.slots[slot].automations[param].param_path);
    191         }
    192         else
    193                         d.reply(d.loc, "s", a.slots[slot].automations[param].param_path);
    194         rEnd},
    195     {"clear:", rDoc("Clear automation param"), 0,
    196         rBegin;
    197         int slot = d.idx[1];
    198         int param = d.idx[0];
    199         a.clearSlotSub(slot, param);
    200         rEnd},
    201     {"mapping/", 0, &mapping_ports,
    202         rBegin;
    203         (void) a;
    204         SNIP;
    205         mapping_ports.dispatch(msg, d);
    206         rEnd},
    207 
    208     //{"mapping", rDoc("Parameter mapping control"), 0,
    209     //    rBegin;
    210     //    int slot  = d.idx[1];
    211     //    int param = d.idx[0];
    212     //    if(!strcmp("b", rtosc_argument_string(msg))) {
    213     //        int    len  = rtosc_argument(msg, 0).b.len / sizeof(float);
    214     //        float *data = (float*)rtosc_argument(msg, 0).b.data;
    215     //    } else {
    216     //        d.reply(d.loc, "b",
    217     //                a.slots[slot].automations[param].map.npoints*sizeof(float),
    218     //                a.slots[slot].automations[param].map.control_points);
    219     //    }
    220     //    rEnd},
    221 };
    222 
    223 static const Ports slot_ports = {
    224     //{"learn-binding:s", rDoc("Create binding for automation path with midi-learn"), 0,
    225     //    rBegin;
    226     //    (void) m;
    227     //    //m->automate.createBinding(rtosc_argument(msg, 0).i,
    228     //    //                          rtosc_argument(msg, 1).s,
    229     //    //                          rtosc_argument(msg, 2).T);
    230     //    rEnd},
    231     //{"create-binding:s", rDoc("Create binding for automation path"), 0,
    232     //    rBegin;
    233     //    m->automate.createBinding(rtosc_argument(msg, 0).i,
    234     //                              rtosc_argument(msg, 1).s,
    235     //                              rtosc_argument(msg, 2).T);
    236     //    rEnd},
    237     {"value::f", rProp(no learn) rProp(parameter) rMap(default, 0.f) rLinear(0, 1) rDoc("Access current value in slot 'i' (0..1)"), 0,
    238         rBegin;
    239         int num = d.idx[0];
    240         if(!strcmp("f",rtosc_argument_string(msg))) {
    241             a.setSlot(num, rtosc_argument(msg, 0).f);
    242             d.broadcast(d.loc, "f", a.getSlot(num));
    243         } else
    244             d.reply(d.loc, "f", a.getSlot(num));
    245         rEnd},
    246 
    247     {"name::s", rProp(parameter) rDoc("Access name of automation slot"), 0,
    248         rBegin;
    249         int num = d.idx[0];
    250         if(!strcmp("s",rtosc_argument_string(msg))) {
    251             a.setName(num, rtosc_argument(msg, 0).s);
    252             d.broadcast(d.loc, "s", a.getName(num));
    253         } else
    254             d.reply(d.loc, "s", a.getName(num));
    255         rEnd},
    256     {"midi-cc::i", rProp(parameter) rMap(default, -1) rDoc("Access assigned midi CC slot") , 0,
    257         rBegin;
    258         int slot = d.idx[0];
    259         if(rtosc_narguments(msg)) {
    260             a.slots[slot].midi_cc = rtosc_argument(msg, 0).i;
    261             d.broadcast(d.loc, "i", a.slots[slot].midi_cc);
    262         } else
    263             d.reply(d.loc, "i", a.slots[slot].midi_cc);
    264 
    265         rEnd},
    266     {"midi-nrpn::i", rProp(parameter) rMap(default, -1) rDoc("Access assigned midi NRPN slot") , 0,
    267         rBegin;
    268         int slot = d.idx[0];
    269         if(rtosc_narguments(msg)) {
    270             a.slots[slot].midi_nrpn = rtosc_argument(msg, 0).i;
    271             d.broadcast(d.loc, "i", a.slots[slot].midi_nrpn);
    272         } else
    273             d.reply(d.loc, "i", a.slots[slot].midi_nrpn);
    274 
    275         rEnd},
    276     {"active::T:F",  rProp(parameter) rDefault(false) rDoc("If Slot is enabled"), 0,
    277         rBegin;
    278         int slot = d.idx[0];
    279         if(rtosc_narguments(msg)) {
    280             a.slots[slot].active = rtosc_argument(msg, 0).T;
    281             d.broadcast(d.loc, a.slots[slot].active ? "T" : "F");
    282         }
    283         else
    284             d.reply(d.loc, a.slots[slot].active ? "T" : "F");
    285         rEnd},
    286     {"learning::i", rProp(parameter) rMap(default, -1) rDoc("If slot is trying to find a midi learn binding"), 0,
    287         rBegin;
    288         int slot = d.idx[0];
    289         d.reply(d.loc, "i", a.slots[slot].learning);
    290         rEnd},
    291     {"clear:", rDoc("Clear automation slot"), 0,
    292         rBegin;
    293         int slot = d.idx[0];
    294         a.clearSlot(slot);
    295         rEnd},
    296     {"param#4/", rDoc("Info on individual param mappings"), &auto_param_ports,
    297         rBegin;
    298         (void)a;
    299         d.push_index(get_next_int(msg));
    300         SNIP;
    301         auto_param_ports.dispatch(msg, d);
    302         d.pop_index();
    303         rEnd},
    304 };
    305 
    306 static const Ports automate_ports = {
    307     {"active-slot::i", rProp(parameter) rMap(min, -1) rMap(max, 16) rDoc("Active Slot for macro learning"), 0,
    308         rBegin;
    309         if(!strcmp("i",rtosc_argument_string(msg))) {
    310             a.active_slot = rtosc_argument(msg, 0).i;
    311             d.broadcast(d.loc, "i", a.active_slot);
    312         } else
    313             d.reply(d.loc, "i", a.active_slot);
    314         rEnd},
    315     {"learn-binding-new-slot:s", rDoc("Learn a parameter assigned to a new slot"), 0,
    316         rBegin;
    317         int free_slot = a.free_slot();
    318         if(free_slot >= 0) {
    319             a.createBinding(free_slot, rtosc_argument(msg, 0).s, true);
    320             a.active_slot = free_slot;
    321         }
    322         rEnd},
    323     {"learn-binding-same-slot:s", rDoc("Learn a parameter appending to the active-slot"), 0,
    324         rBegin;
    325         if(a.active_slot >= 0)
    326             a.createBinding(a.active_slot, rtosc_argument(msg, 0).s, true);
    327         rEnd},
    328     {"slot#16/", rDoc("Parameters of individual automation slots"), &slot_ports,
    329         rBegin;
    330         (void)a;
    331         d.push_index(get_next_int(msg));
    332         SNIP;
    333         slot_ports.dispatch(msg, d);
    334         d.pop_index();
    335         rEnd},
    336     {"clear", rDoc("Clear all automation slots"), 0,
    337         rBegin;
    338         for(int i=0; i<a.nslots; ++i)
    339             a.clearSlot(i);
    340         rEnd},
    341     {"load-blob:b", rProp(internal) rDoc("Load blob from middleware"), 0,
    342         rBegin;
    343         auto &b = **(rtosc::AutomationMgr **)rtosc_argument(msg, 0).b.data;
    344         //XXX this code should likely be in rtosc
    345         for(int i=0; i<a.nslots; ++i) {
    346             auto &slota = a.slots[i];
    347             auto &slotb = b.slots[i];
    348             std::swap(slota.learning, slotb.learning);
    349             std::swap(slota.midi_cc,  slotb.midi_cc);
    350             std::swap(slota.used,     slotb.used);
    351             std::swap(slota.active,   slotb.active);
    352             for(int j=0; j<a.per_slot; ++j) {
    353                 auto &aa = slota.automations[j];
    354                 auto &ab = slotb.automations[j];
    355                 std::swap(aa.used, ab.used);
    356                 std::swap(aa.active, ab.active);
    357                 std::swap(aa.param_path, ab.param_path);
    358                 std::swap(aa.param_min, ab.param_min);
    359                 std::swap(aa.param_max, ab.param_max);
    360                 std::swap(aa.param_step, ab.param_step);
    361                 std::swap(aa.param_type, ab.param_type);
    362                 std::swap(aa.map.offset, ab.map.offset);
    363                 std::swap(aa.map.gain, ab.map.gain);
    364                 std::swap(aa.map.upoints, ab.map.upoints);
    365                 for(int k=0; k<aa.map.npoints; ++k)
    366                     std::swap(aa.map.control_points[k], ab.map.control_points[k]);
    367             }
    368         }
    369         {
    370             rtosc::AutomationMgr* ptr = &b;
    371             d.reply("/free", "sb", "rtosc::AutomationMgr", sizeof(rtosc::AutomationMgr*), &ptr);
    372         }
    373         rEnd},
    374 };
    375 
    376 #undef  rBegin
    377 #undef  rEnd
    378 #define rBegin [](const char *msg, RtData &d) { (void)msg; Master *m = (Master*)d.obj
    379 #define rEnd }
    380 
    381 static const Ports watchPorts = {
    382     {"add:s", rDoc("Add synthesis state to watch"), 0,
    383         rBegin;
    384         if(!m->watcher.active(rtosc_argument(msg,0).s))
    385             m->watcher.add_watch(rtosc_argument(msg,0).s);
    386         rEnd},
    387 };
    388 
    389 
    390 extern const Ports bankPorts;
    391 static const Ports master_ports = {
    392     rString(last_xmz, XMZ_PATH_MAX, "File name for last name loaded if any."),
    393     rRecursp(part, 16, "Part"),//NUM_MIDI_PARTS
    394     rRecursp(sysefx, 4, "System Effect"),//NUM_SYS_EFX
    395     rRecursp(insefx, 8, "Insertion Effect"),//NUM_INS_EFX
    396     rRecur(HDDRecorder, "HDD recorder"),
    397     rRecur(microtonal, "Microtonal Mapping Functionality"),
    398     rRecur(ctl, "Controller"),
    399     rArrayOption(Pinsparts, NUM_INS_EFX, rOpt(-2, Master), rOpt(-1, Off),
    400                  rOptions(Part1, Part2, Part3, Part4, Part5, Part6,
    401                           Part7, Part8, Part9, Part10, Part11, Part12,
    402                           Part13, Part14, Part15, Part16) rDefault([Off ...]),
    403                  "Part to insert part onto"),
    404     {"Pkeyshift::i", rShort("key shift") rProp(parameter) rLinear(0,127)
    405         rDefault(64) rDoc("Global Key Shift"), 0, [](const char *m, RtData&d) {
    406         if(rtosc_narguments(m)==0) {
    407             d.reply(d.loc, "i", ((Master*)d.obj)->Pkeyshift);
    408         } else if(rtosc_narguments(m)==1 && rtosc_type(m,0)=='i') {
    409             ((Master*)d.obj)->setPkeyshift(limit<char>(rtosc_argument(m,0).i,0,127));
    410             d.broadcast(d.loc, "i", ((Master*)d.obj)->Pkeyshift);}}},
    411     {"echo", rDoc("Hidden port to echo messages"), 0, [](const char *m, RtData&d) {
    412        d.reply(m-1);}},
    413     {"get-vu:", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) {
    414        Master *m = (Master*)d.obj;
    415        d.reply("/vu-meter", "bb", sizeof(m->vu), &m->vu, sizeof(float)*NUM_MIDI_PARTS, m->vuoutpeakpartl);}},
    416     {"vu-meter:", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) {
    417        Master *m = (Master*)d.obj;
    418        char        types[6+2*NUM_MIDI_PARTS+1] = {};
    419        rtosc_arg_t  args[6+2*NUM_MIDI_PARTS+1];
    420        for(int i=0; i<6+2*NUM_MIDI_PARTS; ++i)
    421            types[i] = 'f';
    422        args[0].f = m->vu.outpeakl;
    423        args[1].f = m->vu.outpeakr;
    424        args[2].f = m->vu.maxoutpeakl;
    425        args[3].f = m->vu.maxoutpeakr;
    426        args[4].f = m->vu.rmspeakl;
    427        args[5].f = m->vu.rmspeakr;
    428        for(int i=0; i<NUM_MIDI_PARTS; ++i) {
    429            args[6 + 2 * i].f = m->vuoutpeakpartl[i];
    430            args[6 + 2 * i + 1].f = m->vuoutpeakpartr[i];
    431        }
    432        d.replyArray("/vu-meter", types, args);}},
    433     {"reset-vu:", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) {
    434        Master *m = (Master*)d.obj;
    435        m->vuresetpeaks();}},
    436     {"load-part:ib", rProp(internal) rDoc("Load Part From Middleware"), 0, [](const char *msg, RtData &d) {
    437        Master *m =  (Master*)d.obj;
    438        Part   *p = *(Part**)rtosc_argument(msg, 1).b.data;
    439        int     i = rtosc_argument(msg, 0).i;
    440        m->part[i]->cloneTraits(*p);
    441        m->part[i]->kill_rt();
    442        d.reply("/free", "sb", "Part", sizeof(void*), &m->part[i]);
    443        m->part[i] = p;
    444        p->initialize_rt();
    445        memset(m->activeNotes, 0, sizeof(m->activeNotes));
    446        }},
    447     {"active_keys:", rProp("Obtain a list of active notes"), 0,
    448         rBegin;
    449         char keys[129] = {};
    450         for(int i=0; i<128; ++i)
    451             keys[i] = m->activeNotes[i] ? 'T' : 'F';
    452         d.broadcast(d.loc, keys);
    453         rEnd},
    454     {"Pvolume::i", rShort("volume") rProp(parameter) rLinear(0,127)
    455         rDefault(80) rDoc("Master Volume"), 0,
    456         [](const char *m, rtosc::RtData &d) {
    457         if(rtosc_narguments(m)==0) {
    458             d.reply(d.loc, "i", (int) roundf(96.0f * ((Master*)d.obj)->Volume / 40.0f + 96.0f));
    459         } else if(rtosc_narguments(m)==1 && rtosc_type(m,0)=='i') {
    460             ((Master *)d.obj)->Volume  = ((Master *)d.obj)->volume127ToFloat(limit<unsigned char>(rtosc_argument(m, 0).i, 0, 127));
    461              d.broadcast(d.loc, "i", limit<char>(rtosc_argument(m, 0).i, 0, 127));
    462         }}},
    463     {"volume::i", rShort("volume") rProp(parameter) rLinear(0,127)
    464            rDoc("Master Volume"), 0,
    465            [](const char *m, rtosc::RtData &d) {
    466            Master *master = (Master *)d.obj;
    467            if(rtosc_narguments(m)==0) {
    468                 d.reply(d.loc, "i", (int) roundf(96.0f * master->Volume / 40.0f + 96.0f));
    469             } else if (rtosc_narguments(m)==1 && rtosc_type(m,0)=='i') {
    470                master->Volume  = master->volume127ToFloat(limit<unsigned char>(rtosc_argument(m, 0).i, 0, 127));
    471                d.broadcast(d.loc, "i", limit<char>(rtosc_argument(m, 0).i, 0, 127));
    472            }}},
    473     rParamF(Volume, rShort("volume"), rDefault(-6.67 (-0x1.aaaaacp+2)), rLinear(-40.0f,13.3333f),
    474              rUnit(dB), "Master Volume"),
    475     {"Psysefxvol#" STRINGIFY(NUM_SYS_EFX) "/::i", 0, &sysefxPort,
    476         [](const char *msg, rtosc::RtData &d) {
    477             SNIP;
    478             sysefxPort.dispatch(msg, d);
    479         }},
    480     {"sysefxfrom#" STRINGIFY(NUM_SYS_EFX) "/", rDoc("Routing Between System Effects"), &sysefsendto,
    481         [](const char *msg, RtData&d) {
    482             SNIP;
    483             sysefsendto.dispatch(msg, d);
    484         }},
    485     {"noteOn:iii:iiif", rDoc("Noteon Event"), 0,
    486         [](const char *m,RtData &d){
    487             Master *M =  (Master*)d.obj;
    488             if (rtosc_narguments(m) > 3)
    489                 /* Manually specify the frequency as 4th argument */
    490                 M->noteOn(rtosc_argument(m,0).i,rtosc_argument(m,1).i,rtosc_argument(m,2).i,rtosc_argument(m,3).f);
    491             else
    492                 /* Standard MIDI noteOn */
    493                 M->noteOn(rtosc_argument(m,0).i,rtosc_argument(m,1).i,rtosc_argument(m,2).i);
    494         }},
    495 
    496     {"noteOff:ii", rDoc("Noteoff Event"), 0,
    497         [](const char *m,RtData &d){
    498             Master *M =  (Master*)d.obj;
    499             M->noteOff(rtosc_argument(m,0).i,rtosc_argument(m,1).i);}},
    500     {"virtual_midi_cc:iii", rDoc("MIDI CC Event"), 0,
    501         [](const char *m,RtData &d){
    502             Master *M =  (Master*)d.obj;
    503             M->setController(rtosc_argument(m,0).i,rtosc_argument(m,1).i,rtosc_argument(m,2).i);}},
    504     {"setController:iii", rDoc("MIDI CC Event"), 0,
    505         [](const char *m,RtData &d){
    506             Master *M =  (Master*)d.obj;
    507             M->setController(rtosc_argument(m,0).i,rtosc_argument(m,1).i,rtosc_argument(m,2).i);}},
    508     {"tempo::i", rProp(parameter) rDefault(120) rShort("Tempo") rUnit(BPM) rDoc("Tempo / Beats per minute") rLinear(40, 200), 0,
    509         rBegin;
    510         if(!strcmp("i",rtosc_argument_string(msg))) {
    511             m->time.tempo = rtosc_argument(msg, 0).i;
    512             d.broadcast(d.loc, "i", m->time.tempo);
    513         } else
    514             d.reply(d.loc, "i", m->time.tempo);
    515         rEnd},
    516     {"Panic:", rDoc("Stop all sound"), 0,
    517         [](const char *, RtData &d) {
    518             Master &M =  *(Master*)d.obj;
    519             M.ShutUp();
    520         }},
    521     {"freeze_state:", rProp(internal) rDoc("Disable OSC event handling\n"
    522             "This sets up a read-only mode from which it's safe for another"
    523             " thread to save parameters"), 0,
    524         [](const char *,RtData &d) {
    525             Master *M =  (Master*)d.obj;
    526             std::atomic_thread_fence(std::memory_order_release);
    527             M->frozenState = true;
    528             d.reply("/state_frozen", "");}},
    529     {"thaw_state:", rProp(internal) rDoc("Resume handling OSC messages\n"
    530             "See /freeze_state for more information"), 0,
    531         [](const char *,RtData &d) {
    532             Master *M =  (Master*)d.obj;
    533             M->frozenState = false;}},
    534     {"midi-learn/", rDoc("MIDI Learn Classic"), &rtosc::MidiMapperRT::ports,
    535         [](const char *msg, RtData &d) {
    536             Master *M =  (Master*)d.obj;
    537             SNIP;
    538             printf("residue message = <%s>\n", msg);
    539             d.obj = &M->midi;
    540             rtosc::MidiMapperRT::ports.dispatch(msg,d);}},
    541     {"automate/", rDoc("MIDI Learn/Plugin Automation support"), &automate_ports,
    542         [](const char *msg, RtData &d) {
    543             SNIP;
    544             d.obj = (void*)&((Master*)d.obj)->automate;
    545             automate_ports.dispatch(msg, d);
    546             }},
    547     {"close-ui:", rDoc("Request to close the unique connection named \"GUI\""), 0,
    548         [](const char *, RtData &d) {
    549        d.reply("/close-ui", "");}},
    550     {"add-rt-memory:bi", rProp(internal) rDoc("Add Additional Memory To RT MemPool"), 0,
    551         [](const char *msg, RtData &d)
    552         {
    553             Master &m = *(Master*)d.obj;
    554             char   *mem = *(char**)rtosc_argument(msg, 0).b.data;
    555             int     i = rtosc_argument(msg, 1).i;
    556             m.memory->addMemory(mem, i);
    557             m.pendingMemory = false;
    558         }},
    559     {"samplerate:", rMap(unit, Hz) rDoc("Get synthesizer sample rate"), 0, [](const char *, RtData &d) {
    560             Master &m = *(Master*)d.obj;
    561             d.reply("/samplerate", "f", m.synth.samplerate_f);
    562         }},
    563     {"oscilsize:", rDoc("Get synthesizer oscillator size"), 0, [](const char *, RtData &d) {
    564             Master &m = *(Master*)d.obj;
    565             d.reply("/oscilsize", "f", m.synth.oscilsize_f);
    566             d.reply("/oscilsize", "i", m.synth.oscilsize);
    567         }},
    568     {"undo_pause:",rProp(internal) rDoc("pause undo event recording"),0,
    569         [](const char *, rtosc::RtData &d) {d.reply("/undo_pause", "");}},
    570     {"undo_resume:",rProp(internal) rDoc("resume undo event recording"),0,
    571         [](const char *, rtosc::RtData &d) {d.reply("/undo_resume", "");}},
    572     {"last_dnd::s", rProp(internal) rDoc("Last Drag and Drop OSC path"),0,
    573         rBOIL_BEGIN
    574             if(!strcmp("", args)) {
    575                 data.reply(loc, "c", obj->dnd_buffer);
    576                 *obj->dnd_buffer = 0;
    577             } else {
    578                 assert(!*obj->dnd_buffer);
    579                 const char* var = rtosc_argument(msg, 0).s;
    580                 printf("receiving /last_dnd %s\n",var);
    581                 strncpy(obj->dnd_buffer, var, Master::dnd_buffer_size-1);
    582             }
    583         rBOIL_END },
    584     {"config/", rNoDefaults
    585         rDoc("Top Level Application Configuration Parameters"),
    586         &Config::ports, [](const char *, rtosc::RtData &d){d.forward();}},
    587     {"presets/", rDoc("Parameter Presets"), &preset_ports, rBOIL_BEGIN
    588         SNIP
    589             preset_ports.dispatch(msg, data);
    590         rBOIL_END},
    591     {"watch/", rDoc("Interface to grab out live synthesis state"), &watchPorts,
    592         rBOIL_BEGIN;
    593         SNIP;
    594         watchPorts.dispatch(msg, data);
    595         rBOIL_END},
    596     {"bank/", rDoc("Controls for instrument banks"), &bankPorts,
    597             [](const char*,RtData&) {}},
    598     {"learn:s", rProp(deprecated) rDoc("MIDI Learn"), 0,
    599         rBegin;
    600         int free_slot = m->automate.free_slot();
    601         if(free_slot >= 0) {
    602             m->automate.createBinding(free_slot, rtosc_argument(msg, 0).s, true);
    603             m->automate.active_slot = free_slot;
    604         }
    605         rEnd},
    606 };
    607 
    608 #undef rBegin
    609 #undef rEnd
    610 
    611 const Ports &Master::ports = master_ports;
    612 
    613 class DataObj:public rtosc::RtData
    614 {
    615     public:
    616         DataObj(char *loc_, size_t loc_size_, void *obj_, rtosc::ThreadLink *bToU_)
    617         {
    618             memset(loc_, 0, loc_size_);
    619             loc      = loc_;
    620             loc_size = loc_size_;
    621             obj      = obj_;
    622             bToU     = bToU_;
    623             forwarded = false;
    624         }
    625 
    626         virtual void replyArray(const char *path, const char *args, rtosc_arg_t *vals) override
    627         {
    628             char *buffer = bToU->buffer();
    629             rtosc_amessage(buffer,bToU->buffer_size(),path,args,vals);
    630             reply(buffer);
    631         }
    632         virtual void reply(const char *path, const char *args, ...) override
    633         {
    634             va_list va;
    635             va_start(va,args);
    636             char *buffer = bToU->buffer();
    637             rtosc_vmessage(buffer,bToU->buffer_size(),path,args,va);
    638             reply(buffer);
    639             va_end(va);
    640         }
    641         virtual void reply(const char *msg) override
    642         {
    643             if(rtosc_message_length(msg, -1) == 0)
    644                 fprintf(stderr, "Warning: Invalid Rtosc message '%s'\n", msg);
    645             bToU->raw_write(msg);
    646         }
    647         virtual void broadcast(const char *path, const char *args, ...) override{
    648             va_list va;
    649             va_start(va,args);
    650             reply("/broadcast", "");
    651             char *buffer = bToU->buffer();
    652             rtosc_vmessage(buffer,bToU->buffer_size(),path,args,va);
    653             reply(buffer);
    654             va_end(va);
    655         }
    656         virtual void broadcast(const char *msg) override
    657         {
    658             reply("/broadcast", "");
    659             reply(msg);
    660         }
    661 
    662         virtual void forward(const char *reason) override
    663         {
    664             assert(message);
    665             (void) reason;
    666             reply("/forward", "");
    667             printf("forwarding '%s'\n", message);
    668             forwarded = true;
    669         }
    670         bool forwarded;
    671     private:
    672         rtosc::ThreadLink *bToU;
    673 };
    674 
    675 vuData::vuData(void)
    676     :outpeakl(0.0f), outpeakr(0.0f), maxoutpeakl(0.0f), maxoutpeakr(0.0f),
    677       rmspeakl(0.0f), rmspeakr(0.0f), clipped(0)
    678 {}
    679 
    680 void Master::saveAutomation(XMLwrapper &xml, const rtosc::AutomationMgr &midi)
    681 {
    682     xml.beginbranch("automation");
    683     {
    684         XmlNode metadata("mgr-info");
    685         metadata["nslots"]       = to_s(midi.nslots);
    686         metadata["nautomations"] = to_s(midi.per_slot);
    687         metadata["ncontrol"]     = to_s(midi.slots[0].automations[0].map.npoints);
    688         xml.add(metadata);
    689 
    690         for(int i=0; i<midi.nslots; ++i) {
    691             const auto &slot = midi.slots[i];
    692             if(!slot.used)
    693                 continue;
    694             xml.beginbranch("slot", i);
    695             XmlNode params("params");
    696             params["midi-cc"] = to_s(slot.midi_cc);
    697             params["name"] = to_s(slot.name);
    698             xml.add(params);
    699             for(int j=0; j<midi.per_slot; ++j) {
    700                 const auto &au = slot.automations[j];
    701                 if(!au.used)
    702                     continue;
    703                 xml.beginbranch("automation", j);
    704                 XmlNode automation("params");
    705                 automation["path"] = au.param_path;
    706                 XmlNode mapping("mapping");
    707                 mapping["gain"]   = to_s(au.map.gain);
    708                 mapping["offset"] = to_s(au.map.offset);
    709                 xml.add(automation);
    710                 xml.add(mapping);
    711                 xml.endbranch();
    712             }
    713 
    714             xml.endbranch();
    715         }
    716     }
    717     xml.endbranch();
    718 }
    719 
    720 void Master::loadAutomation(XMLwrapper &xml, rtosc::AutomationMgr &midi)
    721 {
    722     //Clear out old data
    723     for(int i=0; i<midi.nslots; ++i)
    724         midi.clearSlot(i);
    725 
    726     if(xml.enterbranch("automation")) {
    727         for(int i=0; i<midi.nslots; ++i) {
    728             auto &slot = midi.slots[i];
    729             if(xml.enterbranch("slot", i)) {
    730                 for(int j=0; j<midi.per_slot; ++j) {
    731                     if(xml.enterbranch("automation", j)) {
    732                         float gain       = 1.0;
    733                         float offset     = 0.0;
    734                         std::string path = "";
    735                         for(auto node:xml.getBranch()) {
    736                             if(node.name == "params")
    737                                 path = node["path"];
    738                             else if(node.name == "mapping") {
    739                                 gain   = atof(node["gain"].c_str());
    740                                 offset = atof(node["offset"].c_str());
    741                             }
    742                         }
    743                         printf("createBinding(%d, %s, false)\n", i, path.c_str());
    744                         midi.createBinding(i, path.c_str(), false);
    745                         midi.setSlotSubGain(i, j, gain);
    746                         midi.setSlotSubOffset(i, j, offset);
    747                         midi.updateMapping(i, j);
    748                         xml.exitbranch();
    749                     }
    750                 }
    751                 for(auto node:xml.getBranch())
    752                 {
    753                     if(node.name == "params")
    754                     {
    755                         slot.midi_cc = atoi(node["midi-cc"].c_str());
    756                         if(node["name"] != "")
    757                         {
    758                             strncpy(slot.name, node["name"].c_str(), sizeof(slot.name) - 1);
    759                         }
    760                     }
    761                 }
    762                 xml.exitbranch();
    763             }
    764         }
    765         xml.exitbranch();
    766     }
    767 }
    768 
    769 Master::Master(const SYNTH_T &synth_, Config* config)
    770     :HDDRecorder(synth_), time(synth_), sync(), ctl(synth_, &time),
    771     microtonal(config->cfg.GzipCompression), bank(config),
    772     automate(16,4,8),
    773     frozenState(false), pendingMemory(false),
    774     synth(synth_), gzip_compression(config->cfg.GzipCompression)
    775 {
    776     SaveFullXml=(config->cfg.SaveFullXml==1);
    777     bToU = NULL;
    778     uToB = NULL;
    779 
    780     sync = new Sync();
    781 
    782     // set default tempo
    783     time.tempo = 120;
    784     time.bar = 0;
    785     time.beat = 0;
    786     time.tick = 0.0f;
    787     time.bpm = 0.0f;
    788 
    789     //Setup MIDI Learn
    790     automate.set_ports(master_ports);
    791     automate.set_instance(this);
    792     midi.frontend = [this](const char *msg) {bToU->raw_write(msg);};
    793     midi.backend  = [this](const char *msg) {applyOscEvent(msg);};
    794     automate.backend  = [this](const char *msg) {applyOscEvent(msg);};
    795 
    796     memory = new AllocatorClass();
    797     swaplr = 0;
    798     off  = 0;
    799     smps = 0;
    800     bufl = new float[synth.buffersize];
    801     bufr = new float[synth.buffersize];
    802 
    803     last_xmz[0] = 0;
    804     fft = new FFTwrapper(synth.oscilsize);
    805 
    806     shutup = 0;
    807     for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
    808         vuoutpeakpartl[npart] = 1e-9;
    809         vuoutpeakpartr[npart] = 1e-9;
    810         fakepeakpart[npart]  = 0;
    811     }
    812 
    813 
    814     ScratchString ss;
    815     for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
    816     {
    817         part[npart] = new Part(*memory, synth, time, sync, config->cfg.GzipCompression,
    818                                config->cfg.Interpolation, &microtonal, fft, &watcher,
    819                                (ss+"/part"+npart+"/").c_str);
    820         smoothing_part_l[npart].sample_rate( synth.samplerate );
    821         smoothing_part_l[npart].reset_on_next_apply( true ); /* necessary to make CI tests happy, otherwise of no practical use */
    822         smoothing_part_r[npart].sample_rate( synth.samplerate );
    823         smoothing_part_r[npart].reset_on_next_apply( true ); /* necessary to make CI tests happy, otherwise of no practical use */
    824     }
    825 
    826     smoothing.sample_rate( synth.samplerate );
    827     smoothing.reset_on_next_apply( true ); /* necessary to make CI tests happy, otherwise of no practical use */
    828 
    829     //Insertion Effects init
    830     for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
    831         insefx[nefx] = new EffectMgr(*memory, synth, 1, &time, sync);
    832 
    833     //System Effects init
    834     for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx)
    835         sysefx[nefx] = new EffectMgr(*memory, synth, 0, &time, sync);
    836 
    837     //Note Visualization
    838     memset(activeNotes, 0, sizeof(activeNotes));
    839 
    840     defaults();
    841 
    842     mastercb = 0;
    843     mastercb_ptr = 0;
    844 }
    845 
    846 bool Master::applyOscEvent(const char *msg, float *outl, float *outr,
    847                            bool offline, bool nio, DataObj& d, int msg_id,
    848                            Master* master_from_mw)
    849 {
    850     if(!strcmp(msg, "/load-master")) {
    851         Master *this_master = master_from_mw ? master_from_mw : this;
    852         Master *new_master  = *(Master**)rtosc_argument(msg, 0).b.data;
    853         // This can not fail anymore, but just to be sure...
    854         assert(new_master != this_master);
    855 
    856         /*
    857          * WARNING: Do not use anything from "this" below, use "this_master"
    858          */
    859 
    860         if(!offline)
    861             new_master->AudioOut(outl, outr);
    862         if(nio)
    863             Nio::masterSwap(new_master);
    864         if (this_master->hasMasterCb()) {
    865             this_master->mastercb(this_master->mastercb_ptr, new_master);
    866         }
    867         bToU->write("/free", "sb", "Master", sizeof(Master*), &this_master);
    868         masterSwitchUpcoming = false;
    869         return false;
    870     } else if(!strcmp(msg, "/switch-master")) {
    871         // if the other stuff from load-master is needed optionally
    872         // (currently, it is not needed anywhere)
    873         // add booleans to the parameters of "/switch-master"
    874         Master *new_master  = *(Master**)rtosc_argument(msg, 0).b.data;
    875         if (hasMasterCb())
    876             mastercb(mastercb_ptr, new_master);
    877         masterSwitchUpcoming = false;
    878         return false;
    879     }
    880 
    881     //XXX yes, this is not realtime safe, but it is useful...
    882     if(strcmp(msg, "/get-vu") && false) {
    883         fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 5 + 30, 0 + 40);
    884         if(msg_id > 0)
    885             fprintf(stdout, "backend[%d]: '%s'<%s>\n", msg_id, msg,
    886                     rtosc_argument_string(msg));
    887         else
    888             fprintf(stdout, "backend[*]: '%s'<%s>\n", msg,
    889                     rtosc_argument_string(msg));
    890         fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
    891     }
    892 
    893     ports.dispatch(msg, d, true);
    894 
    895     if(!d.matches) {
    896         //workaround for requesting voice status
    897         int a=0, b=0, c=0;
    898         char e=0;
    899         if(4 == sscanf(msg, "/part%d/kit%d/adpars/VoicePar%d/Enable%c", &a, &b, &c, &e)) {
    900             d.reply(msg, "F");
    901             d.matches++;
    902         }
    903     }
    904     if(!d.matches && !d.forwarded) {// && !ports.apropos(msg)) {
    905         fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 1, 7 + 30, 0 + 40);
    906         fprintf(stderr, "Unknown address<BACKEND:%s> '%s:%s'\n",
    907                 offline ? "offline" : "online",
    908                 uToB->peak(),
    909                 rtosc_argument_string(uToB->peak()));
    910         fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
    911         if(unknown_address_cb)
    912             unknown_address_cb(unknown_address_cb_ptr, offline, uToB->peak());
    913     }
    914     else if(d.forwarded)
    915         bToU->raw_write(msg);
    916 
    917     return true;
    918 }
    919 
    920 bool Master::applyOscEvent(const char *msg, float *outl, float *outr,
    921                            bool offline, bool nio, int msg_id)
    922 {
    923     char loc_buf[1024];
    924     DataObj d{loc_buf, 1024, this, bToU};
    925     memset(loc_buf, 0, sizeof(loc_buf));
    926     d.matches = 0;
    927 
    928     return applyOscEvent(msg, outl, outr, offline, nio, d, msg_id);
    929 }
    930 
    931 bool Master::applyOscEvent(const char *msg, bool nio, int msg_id)
    932 {
    933     return applyOscEvent(msg, NULL, NULL, true, nio, msg_id);
    934 }
    935 
    936 void Master::defaults()
    937 {
    938     union {float f; uint32_t i;} convert;
    939     convert.i = 0xC0D55556;
    940     Volume = convert.f;
    941     setPkeyshift(64);
    942 
    943     for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
    944         part[npart]->defaults();
    945         part[npart]->partno  = npart % NUM_MIDI_CHANNELS;
    946         part[npart]->Prcvchn = npart % NUM_MIDI_CHANNELS;
    947     }
    948 
    949     partonoff(0, 1); //enable the first part
    950 
    951     for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) {
    952         insefx[nefx]->defaults();
    953         Pinsparts[nefx] = -1;
    954     }
    955 
    956     //System Effects init
    957     for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) {
    958         sysefx[nefx]->defaults();
    959         for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
    960             setPsysefxvol(npart, nefx, 0);
    961 
    962         for(int nefxto = 0; nefxto < NUM_SYS_EFX; ++nefxto)
    963             setPsysefxsend(nefx, nefxto, 0);
    964     }
    965 
    966     microtonal.defaults();
    967     ShutUp();
    968 }
    969 
    970 /*
    971  * Note On Messages (velocity=0 for NoteOff)
    972  */
    973 void Master::noteOn(char chan, note_t note, char velocity, float note_log2_freq)
    974 {
    975     if(velocity) {
    976         sync->notify();
    977         for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
    978             if(chan == part[npart]->Prcvchn) {
    979                 fakepeakpart[npart] = velocity * 2;
    980                 if(part[npart]->Penabled)
    981                     part[npart]->NoteOn(note, velocity, keyshift, note_log2_freq);
    982             }
    983         }
    984         activeNotes[note] = 1;
    985         HDDRecorder.triggernow();
    986     }
    987     else
    988         this->noteOff(chan, note);
    989 }
    990 
    991 /*
    992  * Note Off Messages
    993  */
    994 void Master::noteOff(char chan, note_t note)
    995 {
    996     for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
    997         if((chan == part[npart]->Prcvchn) && part[npart]->Penabled)
    998             part[npart]->NoteOff(note);
    999     activeNotes[note] = 0;
   1000 }
   1001 
   1002 /*
   1003  * Pressure Messages (velocity=0 for NoteOff)
   1004  */
   1005 void Master::polyphonicAftertouch(char chan, note_t note, char velocity)
   1006 {
   1007     for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
   1008         if(chan == part[npart]->Prcvchn)
   1009             if(part[npart]->Penabled)
   1010                 part[npart]->PolyphonicAftertouch(note, velocity);
   1011 }
   1012 
   1013 /*
   1014  * Controllers
   1015  */
   1016 void Master::setController(char chan, int type, int par)
   1017 {
   1018     if(frozenState)
   1019         return;
   1020     automate.handleMidi(chan, type, par);
   1021     midi.handleCC(type, par, chan, false);
   1022     if((type == C_dataentryhi) || (type == C_dataentrylo)
   1023        || (type == C_nrpnhi) || (type == C_nrpnlo)) { //Process RPN and NRPN by the Master (ignore the chan)
   1024         ctl.setparameternumber(type, par);
   1025 
   1026         int parhi = -1, parlo = -1, valhi = -1, vallo = -1;
   1027         if(ctl.getnrpn(&parhi, &parlo, &valhi, &vallo) == 0) { //this is NRPN
   1028             switch(parhi) {
   1029                 case 0x04: //System Effects
   1030                     if(parlo < NUM_SYS_EFX)
   1031                         sysefx[parlo]->seteffectparrt(valhi, vallo);
   1032                     break;
   1033                 case 0x08: //Insertion Effects
   1034                     if(chan == 0 && parlo < NUM_INS_EFX)
   1035                         insefx[parlo]->seteffectparrt(valhi, vallo);
   1036                     else if (chan < NUM_MIDI_PARTS && parlo < NUM_PART_EFX)
   1037                         part[chan-1]->partefx[parlo]->seteffectparrt(valhi, vallo);
   1038                     break;
   1039                 default:
   1040                     midi.handleCC(parhi<<7&parlo,valhi<<7&vallo, chan, true);
   1041                     break;
   1042             }
   1043         }
   1044     } else {  //other controllers
   1045         for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) //Send the controller to all part assigned to the channel
   1046             if((chan == part[npart]->Prcvchn) && (part[npart]->Penabled != 0))
   1047                 part[npart]->SetController(type, par);
   1048 
   1049         if(type == C_allsoundsoff) { //cleanup insertion/system FX
   1050             for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx)
   1051                 sysefx[nefx]->cleanup();
   1052             for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
   1053                 insefx[nefx]->cleanup();
   1054         }
   1055     }
   1056 }
   1057 
   1058 /*
   1059  * Per note controllers
   1060  */
   1061 void Master::setController(char chan, int type, note_t note, float value)
   1062 {
   1063     if(frozenState)
   1064         return;
   1065 
   1066     /* Send the controller to all part assigned to the channel */
   1067     for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
   1068         if((chan == part[npart]->Prcvchn) && (part[npart]->Penabled != 0))
   1069             part[npart]->SetController(type, note, value, keyshift);
   1070 }
   1071 
   1072 void Master::vuUpdate(const float *outl, const float *outr)
   1073 {
   1074     //Peak computation (for vumeters)
   1075     vu.outpeakl = 1e-12;
   1076     vu.outpeakr = 1e-12;
   1077     for(int i = 0; i < synth.buffersize; ++i) {
   1078         if(fabsf(outl[i]) > vu.outpeakl)
   1079             vu.outpeakl = fabsf(outl[i]);
   1080         if(fabsf(outr[i]) > vu.outpeakr)
   1081             vu.outpeakr = fabsf(outr[i]);
   1082     }
   1083     if((vu.outpeakl > 1.0f) || (vu.outpeakr > 1.0f))
   1084         vu.clipped = 1;
   1085     if(vu.maxoutpeakl < vu.outpeakl)
   1086         vu.maxoutpeakl = vu.outpeakl;
   1087     if(vu.maxoutpeakr < vu.outpeakr)
   1088         vu.maxoutpeakr = vu.outpeakr;
   1089 
   1090     //RMS Peak computation (for vumeters)
   1091     vu.rmspeakl = 1e-12;
   1092     vu.rmspeakr = 1e-12;
   1093     for(int i = 0; i < synth.buffersize; ++i) {
   1094         vu.rmspeakl += outl[i] * outl[i];
   1095         vu.rmspeakr += outr[i] * outr[i];
   1096     }
   1097     vu.rmspeakl = sqrt(vu.rmspeakl / synth.buffersize_f);
   1098     vu.rmspeakr = sqrt(vu.rmspeakr / synth.buffersize_f);
   1099 
   1100     //Part Peak computation (for Part vumeters or fake part vumeters)
   1101     for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
   1102         vuoutpeakpartl[npart] = 1.0e-12f;
   1103         vuoutpeakpartr[npart] = 1.0e-12f;
   1104         if(part[npart]->Penabled != 0) {
   1105             float *outl = part[npart]->partoutl,
   1106             *outr = part[npart]->partoutr;
   1107             for(int i = 0; i < synth.buffersize; ++i) {
   1108                 if (fabsf(outl[i]) > vuoutpeakpartl[npart])
   1109                     vuoutpeakpartl[npart] = fabsf(outl[i]);
   1110                 if (fabsf(outr[i]) > vuoutpeakpartr[npart])
   1111                     vuoutpeakpartr[npart] = fabsf(outr[i]);
   1112             }
   1113         }
   1114         else
   1115         if(fakepeakpart[npart] > 1)
   1116             fakepeakpart[npart]--;
   1117     }
   1118 }
   1119 
   1120 /*
   1121  * Enable/Disable a part
   1122  */
   1123 void Master::partonoff(int npart, int what)
   1124 {
   1125     if(npart >= NUM_MIDI_PARTS)
   1126         return;
   1127     if(what == 0) { //disable part
   1128         fakepeakpart[npart]   = 0;
   1129         part[npart]->Penabled = 0;
   1130         part[npart]->cleanup();
   1131         for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) {
   1132             if(Pinsparts[nefx] == npart)
   1133                 insefx[nefx]->cleanup();
   1134         }
   1135     }
   1136     else {  //enabled
   1137         part[npart]->Penabled = 1;
   1138         fakepeakpart[npart]   = 0;
   1139     }
   1140 }
   1141 
   1142 void Master::setMasterChangedCallback(void(*cb)(void*,Master*), void *ptr)
   1143 {
   1144     mastercb     = cb;
   1145     mastercb_ptr = ptr;
   1146 }
   1147 
   1148 void Master::copyMasterCbTo(Master *dest)
   1149 {
   1150     dest->mastercb     = mastercb;
   1151     dest->mastercb_ptr = mastercb_ptr;
   1152 }
   1153 
   1154 bool Master::hasMasterCb() const
   1155 {
   1156     return !!mastercb;
   1157 }
   1158 
   1159 void Master::setAudioCompressor(bool enabled)
   1160 {
   1161     Nio::setAudioCompressor(enabled);
   1162 }
   1163 
   1164 #if 0
   1165 template <class T>
   1166 struct def_skip
   1167 {
   1168         static void skip(const char*& argptr) { argptr += sizeof(T); }
   1169 };
   1170 
   1171 template <class T>
   1172 struct str_skip
   1173 {
   1174         static void skip(const char*& argptr) { while(argptr++); /*TODO: 4 padding */ }
   1175 };
   1176 
   1177 template<class T, class Display = T, template<class TMP> class SkipsizeFunc = def_skip>
   1178 void _dump_prim_arg(const char*& argptr, std::ostream& os)
   1179 {
   1180         os << ' ' << (Display)*(const T*)argptr;
   1181         SkipsizeFunc<T>::skip(argptr);
   1182 }
   1183 
   1184 void dump_msg(const char* ptr, std::ostream& os = std::cerr)
   1185 {
   1186         assert(*ptr == '/');
   1187         os << ptr;
   1188 
   1189         while(*++ptr) ; // skip address
   1190         while(!*++ptr) ; // skip 0s
   1191 
   1192         assert(*ptr == ',');
   1193         os << ' ' << (ptr + 1);
   1194 
   1195         const char* argptr = ptr;
   1196         while(*++argptr) ; // skip type string
   1197         while(!*++argptr) ; // skip 0s
   1198 
   1199         char c;
   1200         while((c = *++ptr))
   1201         {
   1202                 switch(c)
   1203                 {
   1204                         case 'i':
   1205                                 _dump_prim_arg<int32_t>(argptr, os); break;
   1206                         case 'c':
   1207                                 _dump_prim_arg<int32_t, char>(argptr, os); break;
   1208                 //      case 's':
   1209                 //              _dump_prim_arg<char, const char*>(argptr, os); break;
   1210                         default:
   1211                                 exit(1);
   1212                 }
   1213         }
   1214 
   1215 }
   1216 #endif
   1217 int msg_id=0;
   1218 
   1219 bool Master::runOSC(float *outl, float *outr, bool offline,
   1220                     Master* master_from_mw)
   1221 {
   1222     // the following block is only ever entered by 1 thread at a time
   1223     // other threads have to ignore it
   1224     if(!run_osc_in_use.exchange(true)) // exchange returns value before call
   1225     {
   1226         /*
   1227          * WARNING: Do not return without "run_osc_in_use.store(false)"
   1228          */
   1229 
   1230         //Handle user events
   1231         char loc_buf[1024];
   1232         DataObj d{loc_buf, 1024, this, bToU};
   1233         memset(loc_buf, 0, sizeof(loc_buf));
   1234 
   1235         int events = 0;
   1236         for(; uToB && uToB->hasNext() && events < 100; ++msg_id, ++events)
   1237         {
   1238             const char *msg = uToB->read();
   1239             if(! applyOscEvent(msg, outl, outr, offline, true, d, msg_id,
   1240                                master_from_mw) )
   1241             {
   1242                 run_osc_in_use.store(false);
   1243                 return false;
   1244             }
   1245         }
   1246 
   1247         if(automate.damaged) {
   1248             d.broadcast("/damage", "s", "/automate/");
   1249             automate.damaged = 0;
   1250         }
   1251 
   1252         if(events>1 && false)
   1253             fprintf(stderr, "backend: %d events per cycle\n",events);
   1254 
   1255         run_osc_in_use.store(false);
   1256         return true;
   1257     }
   1258     else { return true; /* = no new master */ }
   1259 }
   1260 
   1261 /*
   1262  * Master audio out (the final sound)
   1263  */
   1264 bool Master::AudioOut(float *outl, float *outr)
   1265 {
   1266 
   1267     //Danger Limits
   1268     if(memory->lowMemory(2,1024*1024))
   1269         printf("QUITE LOW MEMORY IN THE RT POOL BE PREPARED FOR WEIRD BEHAVIOR!!\n");
   1270     //Normal Limits
   1271     if(!pendingMemory && memory->lowMemory(6,1024*1024)) {
   1272         printf("Requesting more memory\n");
   1273         bToU->write("/request-memory", "");
   1274         pendingMemory = true;
   1275     }
   1276 
   1277     //work through events
   1278     if(!runOSC(outl, outr, false))
   1279         return false;
   1280 
   1281     //Handle watch points
   1282     if(bToU)
   1283         watcher.write_back = bToU;
   1284     watcher.tick();
   1285 
   1286     //Swaps the Left channel with Right Channel
   1287     if(swaplr)
   1288         swap(outl, outr);
   1289 
   1290     //clean up the output samples (should not be needed?)
   1291     memset(outl, 0, synth.bufferbytes);
   1292     memset(outr, 0, synth.bufferbytes);
   1293 
   1294     //Compute part samples and store them part[npart]->partoutl,partoutr
   1295     //Note: We do this regardless if the part is enabled or not, to allow
   1296     //the part to graciously shut down when disabled.
   1297     for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
   1298         part[npart]->ComputePartSmps();
   1299 
   1300     //Insertion effects
   1301     for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
   1302         if(Pinsparts[nefx] >= 0) {
   1303             int efxpart = Pinsparts[nefx];
   1304             if(part[efxpart]->Penabled)
   1305                 insefx[nefx]->out(part[efxpart]->partoutl,
   1306                                   part[efxpart]->partoutr);
   1307         }
   1308 
   1309     float gainbuf[synth.buffersize];
   1310 
   1311     //Apply the part volumes and pannings (after insertion effects)
   1312     for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
   1313         if(!part[npart]->Penabled)
   1314             continue;
   1315 
   1316         Stereo<float> newvol(part[npart]->gain);
   1317 
   1318         float pan = part[npart]->panning;
   1319         if(pan < 0.5f)
   1320             newvol.r *= pan * 2.0f;
   1321         else
   1322             newvol.l *= (1.0f - pan) * 2.0f;
   1323         //if(npart==0)
   1324         //printf("[%d]vol = %f->%f\n", npart, oldvol.l, newvol.l);
   1325 
   1326 
   1327 
   1328         /* This is where the part volume (and pan) smoothing and application happens */
   1329         if ( smoothing_part_l[npart].apply( gainbuf, synth.buffersize, newvol.l ) )
   1330         {
   1331             for ( int i = 0; i < synth.buffersize; ++i )
   1332                 part[npart]->partoutl[i] *= gainbuf[i];
   1333         }
   1334         else
   1335         {
   1336             for ( int i = 0; i < synth.buffersize; ++i )
   1337                 part[npart]->partoutl[i] *= newvol.l;
   1338         }
   1339 
   1340         if ( smoothing_part_r[npart].apply( gainbuf, synth.buffersize, newvol.r ) )
   1341         {
   1342             for ( int i = 0; i < synth.buffersize; ++i )
   1343                 part[npart]->partoutr[i] *= gainbuf[i];
   1344         }
   1345         else
   1346         {
   1347             for ( int i = 0; i < synth.buffersize; ++i )
   1348                 part[npart]->partoutr[i] *= newvol.r;
   1349         }
   1350     }
   1351 
   1352     //System effects
   1353     for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) {
   1354         if(sysefx[nefx]->geteffect() == 0)
   1355             continue;  //the effect is disabled
   1356 
   1357         float tmpmixl[synth.buffersize];
   1358         float tmpmixr[synth.buffersize];
   1359         //Clean up the samples used by the system effects
   1360         memset(tmpmixl, 0, synth.bufferbytes);
   1361         memset(tmpmixr, 0, synth.bufferbytes);
   1362 
   1363         //Mix the channels according to the part settings about System Effect
   1364         for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
   1365             //skip if the part has no output to effect
   1366             if(Psysefxvol[nefx][npart] == 0)
   1367                 continue;
   1368 
   1369             //skip if the part is disabled
   1370             if(part[npart]->Penabled == 0)
   1371                 continue;
   1372 
   1373             //the output volume of each part to system effect
   1374             const float vol = sysefxvol[nefx][npart];
   1375             for(int i = 0; i < synth.buffersize; ++i) {
   1376                 tmpmixl[i] += part[npart]->partoutl[i] * vol;
   1377                 tmpmixr[i] += part[npart]->partoutr[i] * vol;
   1378             }
   1379         }
   1380 
   1381         // system effect send to next ones
   1382         for(int nefxfrom = 0; nefxfrom < nefx; ++nefxfrom)
   1383             if(Psysefxsend[nefxfrom][nefx] != 0) {
   1384                 const float vol = sysefxsend[nefxfrom][nefx];
   1385                 for(int i = 0; i < synth.buffersize; ++i) {
   1386                     tmpmixl[i] += sysefx[nefxfrom]->efxoutl[i] * vol;
   1387                     tmpmixr[i] += sysefx[nefxfrom]->efxoutr[i] * vol;
   1388                 }
   1389             }
   1390 
   1391         sysefx[nefx]->out(tmpmixl, tmpmixr);
   1392 
   1393         //Add the System Effect to sound output
   1394         const float outvol = sysefx[nefx]->sysefxgetvolume();
   1395         for(int i = 0; i < synth.buffersize; ++i) {
   1396             outl[i] += tmpmixl[i] * outvol;
   1397             outr[i] += tmpmixr[i] * outvol;
   1398         }
   1399     }
   1400 
   1401     //Mix all parts
   1402     for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
   1403         if(part[npart]->Penabled)   //only mix active parts
   1404             for(int i = 0; i < synth.buffersize; ++i) { //the volume did not changed
   1405                 outl[i] += part[npart]->partoutl[i];
   1406                 outr[i] += part[npart]->partoutr[i];
   1407             }
   1408 
   1409     //Insertion effects for Master Out
   1410     for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
   1411         if(Pinsparts[nefx] == -2)
   1412             insefx[nefx]->out(outl, outr);
   1413 
   1414     float vol = dB2rap(Volume);
   1415 
   1416     //Master Volume
   1417     /* this is where the master volume smoothing and application happens */
   1418     if ( smoothing.apply( gainbuf, synth.buffersize, vol ) )
   1419     {
   1420         for ( int i = 0; i < synth.buffersize; ++i )
   1421         {
   1422             outl[i] *= gainbuf[i];
   1423             outr[i] *= gainbuf[i];
   1424         }
   1425     }
   1426     else
   1427     {
   1428         for ( int i = 0; i < synth.buffersize; ++i )
   1429         {
   1430             outl[i] *= vol;
   1431             outr[i] *= vol;
   1432         }
   1433     }
   1434 
   1435     vuUpdate(outl, outr);
   1436 
   1437     //Shutup if it is asked (with fade-out)
   1438     if(shutup) {
   1439         for(int i = 0; i < synth.buffersize; ++i) {
   1440             float tmp = (synth.buffersize_f - i) / synth.buffersize_f;
   1441             outl[i] *= tmp;
   1442             outr[i] *= tmp;
   1443         }
   1444         ShutUp();
   1445     }
   1446 
   1447     //update the global frame timer
   1448     time++;
   1449 
   1450 #ifdef DEMO_VERSION
   1451     double seconds = time.time()*synth.buffersize_f/synth.samplerate_f;
   1452     if(seconds > 10*60) {//10 minute trial
   1453         shutup = true;
   1454         for(int i = 0; i < synth.buffersize; ++i) {
   1455             outl[i] *= 0.0f;
   1456             outr[i] *= 0.0f;
   1457         }
   1458     }
   1459 #endif
   1460 
   1461     //Update pulse
   1462     last_ack = last_beat;
   1463 
   1464 
   1465     return true;
   1466 }
   1467 
   1468 //TODO review the respective code from yoshimi for this
   1469 //If memory serves correctly, libsamplerate was used
   1470 //
   1471 // beatType is not being used yet.
   1472 // but beatsPerBar/beatType could be used to
   1473 // match numerator/denominator along with bpm to plugin host
   1474 
   1475 void Master::GetAudioOutSamples(size_t nsamples,
   1476                                 unsigned samplerate,
   1477                                 float *outl,
   1478                                 float *outr,
   1479                                 int bar,
   1480                                 int beat,
   1481                                 float tick,
   1482                                 float beatsPerBar,
   1483                                 float /*beatType*/,
   1484                                 float bpm,
   1485                                 float PPQ,
   1486                                 bool playing,
   1487                                 size_t frames)
   1488 {
   1489 
   1490     if(bpm) {
   1491         time.hostSamples = frames;
   1492         time.bar = bar;
   1493         time.beat = beat;
   1494         time.tick = tick;
   1495         time.beatsPerBar = beatsPerBar;
   1496         time.tempo = bpm;
   1497         time.bpm = bpm;
   1498         time.ppq = PPQ;
   1499         time.playing = playing;
   1500     }
   1501     else
   1502         time.bpm = 0;
   1503 
   1504     off_t out_off = 0;
   1505 
   1506     //Fail when resampling rather than doing a poor job
   1507     if(synth.samplerate != samplerate) {
   1508         printf("darn it: %d vs %d\n", synth.samplerate, samplerate);
   1509         return;
   1510     }
   1511 
   1512     while(nsamples) {
   1513         //use all available samples
   1514         if(nsamples >= smps) {
   1515             memcpy(outl + out_off, bufl + off, sizeof(float) * smps);
   1516             memcpy(outr + out_off, bufr + off, sizeof(float) * smps);
   1517             nsamples -= smps;
   1518 
   1519             //generate samples
   1520             if (! AudioOut(bufl, bufr))
   1521                 return;
   1522 
   1523             off  = 0;
   1524             out_off  += smps;
   1525             smps = synth.buffersize;
   1526         }
   1527         else {   //use some samples
   1528             memcpy(outl + out_off, bufl + off, sizeof(float) * nsamples);
   1529             memcpy(outr + out_off, bufr + off, sizeof(float) * nsamples);
   1530             smps    -= nsamples;
   1531             off     += nsamples;
   1532             nsamples = 0;
   1533         }
   1534     }
   1535 }
   1536 
   1537 Master::~Master()
   1538 {
   1539     delete []bufl;
   1540     delete []bufr;
   1541 
   1542     for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
   1543         delete part[npart];
   1544     for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
   1545         delete insefx[nefx];
   1546     for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx)
   1547         delete sysefx[nefx];
   1548 
   1549     delete fft;
   1550     delete memory;
   1551 }
   1552 
   1553 
   1554 /*
   1555  * Parameter control
   1556  */
   1557 
   1558 float Master::volume127ToFloat(unsigned char volume_)
   1559 {
   1560     return (volume_ - 96.0f) / 96.0f * 40.0;
   1561 }
   1562 
   1563 void Master::setPkeyshift(char Pkeyshift_)
   1564 {
   1565     Pkeyshift = Pkeyshift_;
   1566     keyshift  = (int)Pkeyshift - 64;
   1567 }
   1568 
   1569 
   1570 void Master::setPsysefxvol(int Ppart, int Pefx, char Pvol)
   1571 {
   1572     Psysefxvol[Pefx][Ppart] = Pvol;
   1573     sysefxvol[Pefx][Ppart]  = powf(0.1f, (1.0f - Pvol / 96.0f) * 2.0f);
   1574 }
   1575 
   1576 void Master::setPsysefxsend(int Pefxfrom, int Pefxto, char Pvol)
   1577 {
   1578     Psysefxsend[Pefxfrom][Pefxto] = Pvol;
   1579     sysefxsend[Pefxfrom][Pefxto]  = powf(0.1f, (1.0f - Pvol / 96.0f) * 2.0f);
   1580 }
   1581 
   1582 
   1583 /*
   1584  * Panic! (Clean up all parts and effects)
   1585  */
   1586 void Master::ShutUp()
   1587 {
   1588     for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
   1589         part[npart]->cleanup();
   1590         fakepeakpart[npart] = 0;
   1591     }
   1592     for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
   1593         insefx[nefx]->cleanup();
   1594     for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx)
   1595         sysefx[nefx]->cleanup();
   1596     memset(activeNotes, 0, sizeof(activeNotes));
   1597     vuresetpeaks();
   1598     shutup = 0;
   1599 }
   1600 
   1601 
   1602 /*
   1603  * Reset peaks and clear the "cliped" flag (for VU-meter)
   1604  */
   1605 void Master::vuresetpeaks()
   1606 {
   1607     vu.outpeakl    = 1e-9;
   1608     vu.outpeakr    = 1e-9;
   1609     vu.maxoutpeakl = 1e-9;
   1610     vu.maxoutpeakr = 1e-9;
   1611     vu.clipped     = 0;
   1612 }
   1613 
   1614 void Master::applyparameters(void)
   1615 {
   1616     for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
   1617         part[npart]->applyparameters();
   1618 }
   1619 
   1620 void Master::initialize_rt(void)
   1621 {
   1622     for(int i=0; i<NUM_SYS_EFX; ++i)
   1623         sysefx[i]->init();
   1624     for(int i=0; i<NUM_INS_EFX; ++i)
   1625         insefx[i]->init();
   1626 
   1627     for(int i=0; i<NUM_MIDI_PARTS; ++i)
   1628         part[i]->initialize_rt();
   1629 }
   1630 
   1631 void Master::add2XML(XMLwrapper& xml)
   1632 {
   1633     xml.addparreal("volume", Volume);
   1634     xml.addpar("key_shift", Pkeyshift);
   1635     xml.addparbool("nrpn_receive", ctl.NRPN.receive);
   1636 
   1637     xml.beginbranch("MICROTONAL");
   1638     microtonal.add2XML(xml);
   1639     xml.endbranch();
   1640 
   1641     if (SaveFullXml) {
   1642         xml.SaveFullXml=true; // save disabled parts
   1643         xml.minimal=false;
   1644     }
   1645 
   1646     saveAutomation(xml, automate);
   1647     for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
   1648         xml.beginbranch("PART", npart);
   1649         part[npart]->add2XML(xml);
   1650         xml.endbranch();
   1651     }
   1652 
   1653     xml.beginbranch("SYSTEM_EFFECTS");
   1654     for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) {
   1655         xml.beginbranch("SYSTEM_EFFECT", nefx);
   1656         xml.beginbranch("EFFECT");
   1657         sysefx[nefx]->add2XML(xml);
   1658         xml.endbranch();
   1659 
   1660         for(int pefx = 0; pefx < NUM_MIDI_PARTS; ++pefx) {
   1661             xml.beginbranch("VOLUME", pefx);
   1662             xml.addpar("vol", Psysefxvol[nefx][pefx]);
   1663             xml.endbranch();
   1664         }
   1665 
   1666         for(int tonefx = nefx + 1; tonefx < NUM_SYS_EFX; ++tonefx) {
   1667             xml.beginbranch("SENDTO", tonefx);
   1668             xml.addpar("send_vol", Psysefxsend[nefx][tonefx]);
   1669             xml.endbranch();
   1670         }
   1671 
   1672 
   1673         xml.endbranch();
   1674     }
   1675     xml.endbranch();
   1676 
   1677     xml.beginbranch("INSERTION_EFFECTS");
   1678     for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) {
   1679         xml.beginbranch("INSERTION_EFFECT", nefx);
   1680         xml.addpar("part", Pinsparts[nefx]);
   1681 
   1682         xml.beginbranch("EFFECT");
   1683         insefx[nefx]->add2XML(xml);
   1684         xml.endbranch();
   1685         xml.endbranch();
   1686     }
   1687 
   1688     xml.endbranch();
   1689 }
   1690 
   1691 
   1692 int Master::getalldata(char **data)
   1693 {
   1694     XMLwrapper xml;
   1695 
   1696     xml.beginbranch("MASTER");
   1697 
   1698     add2XML(xml);
   1699 
   1700     xml.endbranch();
   1701 
   1702     *data = xml.getXMLdata();
   1703     return strlen(*data) + 1;
   1704 }
   1705 
   1706 void Master::putalldata(const char *data)
   1707 {
   1708     XMLwrapper xml;
   1709     if(!xml.putXMLdata(data)) {
   1710         return;
   1711     }
   1712 
   1713     if(xml.enterbranch("MASTER") == 0)
   1714         return;
   1715 
   1716     getfromXML(xml);
   1717 
   1718     xml.exitbranch();
   1719 }
   1720 
   1721 int Master::saveXML(const char *filename)
   1722 {
   1723     XMLwrapper xml;
   1724 
   1725     xml.beginbranch("MASTER");
   1726     add2XML(xml);
   1727     xml.endbranch();
   1728 
   1729     return xml.saveXMLfile(filename, gzip_compression);
   1730 }
   1731 
   1732 
   1733 int Master::loadXML(const char *filename)
   1734 {
   1735     XMLwrapper xml;
   1736 
   1737     if(xml.loadXMLfile(filename) < 0) {
   1738         return -1;
   1739     }
   1740 
   1741     if(xml.enterbranch("MASTER") == 0)
   1742         return -10;
   1743 
   1744     getfromXML(xml);
   1745     xml.exitbranch();
   1746 
   1747     initialize_rt();
   1748     return 0;
   1749 }
   1750 
   1751 void Master::getfromXML(XMLwrapper& xml)
   1752 {
   1753     if (xml.hasparreal("volume")) {
   1754         Volume = xml.getparreal("volume", Volume);
   1755     } else {
   1756         Volume  = volume127ToFloat(xml.getpar127("volume", 0));
   1757     }
   1758     setPkeyshift(xml.getpar127("key_shift", Pkeyshift));
   1759     ctl.NRPN.receive = xml.getparbool("nrpn_receive", ctl.NRPN.receive);
   1760 
   1761 
   1762     part[0]->Penabled = 0;
   1763     for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
   1764         if(xml.enterbranch("PART", npart) == 0)
   1765             continue;
   1766         part[npart]->getfromXML(xml);
   1767         xml.exitbranch();
   1768     }
   1769 
   1770     if(xml.enterbranch("MICROTONAL")) {
   1771         microtonal.getfromXML(xml);
   1772         xml.exitbranch();
   1773     }
   1774 
   1775     loadAutomation(xml, automate);
   1776 
   1777     sysefx[0]->changeeffect(0);
   1778     if(xml.enterbranch("SYSTEM_EFFECTS")) {
   1779         for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) {
   1780             if(xml.enterbranch("SYSTEM_EFFECT", nefx) == 0)
   1781                 continue;
   1782             if(xml.enterbranch("EFFECT")) {
   1783                 sysefx[nefx]->getfromXML(xml);
   1784                 xml.exitbranch();
   1785             }
   1786 
   1787             for(int partefx = 0; partefx < NUM_MIDI_PARTS; ++partefx) {
   1788                 if(xml.enterbranch("VOLUME", partefx) == 0)
   1789                     continue;
   1790                 setPsysefxvol(partefx, nefx,
   1791                               xml.getpar127("vol", Psysefxvol[partefx][nefx]));
   1792                 xml.exitbranch();
   1793             }
   1794 
   1795             for(int tonefx = nefx + 1; tonefx < NUM_SYS_EFX; ++tonefx) {
   1796                 if(xml.enterbranch("SENDTO", tonefx) == 0)
   1797                     continue;
   1798                 setPsysefxsend(nefx, tonefx,
   1799                                xml.getpar127("send_vol",
   1800                                               Psysefxsend[nefx][tonefx]));
   1801                 xml.exitbranch();
   1802             }
   1803             xml.exitbranch();
   1804         }
   1805         xml.exitbranch();
   1806     }
   1807 
   1808 
   1809     if(xml.enterbranch("INSERTION_EFFECTS")) {
   1810         for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) {
   1811             if(xml.enterbranch("INSERTION_EFFECT", nefx) == 0)
   1812                 continue;
   1813             Pinsparts[nefx] = xml.getpar("part",
   1814                                           Pinsparts[nefx],
   1815                                           -2,
   1816                                           NUM_MIDI_PARTS);
   1817             if(xml.enterbranch("EFFECT")) {
   1818                 insefx[nefx]->getfromXML(xml);
   1819                 xml.exitbranch();
   1820             }
   1821             xml.exitbranch();
   1822         }
   1823 
   1824         xml.exitbranch();
   1825     }
   1826 }
   1827 
   1828 static rtosc_version version_in_rtosc_fmt()
   1829 {
   1830     return rtosc_version
   1831     {
   1832         (unsigned char) version.get_major(),
   1833         (unsigned char) version.get_minor(),
   1834         (unsigned char) version.get_revision()
   1835     };
   1836 }
   1837 
   1838 char* Master::getXMLData()
   1839 {
   1840     XMLwrapper xml;
   1841 
   1842     xml.beginbranch("MASTER");
   1843     add2XML(xml);
   1844     xml.endbranch();
   1845 
   1846     return xml.getXMLdata();
   1847 }
   1848 
   1849 // this is being called as a "read only op" directly by the MiddleWare thread;
   1850 // note that the Master itself is frozen
   1851 std::string Master::saveOSC(std::string savefile, std::set<std::string>& alreadyWritten)
   1852 {
   1853     return rtosc::save_to_file(ports, this,
   1854                                nullptr, version_in_rtosc_fmt(), // both unused
   1855                                alreadyWritten,
   1856                                {"non-realtime"}, // excluded non-reatlime ports
   1857                                savefile);
   1858 }
   1859 
   1860 int Master::loadOSCFromStr(const char *file_content,
   1861                            savefile_dispatcher_t* dispatcher)
   1862 {
   1863     return rtosc::load_from_file(file_content,
   1864                                  ports, this,
   1865                                  "ZynAddSubFX", version_in_rtosc_fmt(),
   1866                                  dispatcher);
   1867 }
   1868 
   1869 string loadfile(string fname)
   1870 {
   1871     std::ifstream t(fname.c_str());
   1872     std::string str((std::istreambuf_iterator<char>(t)),
   1873                      std::istreambuf_iterator<char>());
   1874     return str;
   1875 }
   1876 
   1877 int Master::loadOSC(const char *filename, savefile_dispatcher_t* dispatcher)
   1878 {
   1879     int rval = loadOSCFromStr(loadfile(filename).c_str(), dispatcher);
   1880     return rval < 0 ? rval : 0;
   1881 }
   1882 
   1883 
   1884 }