zynaddsubfx

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

EffectMgr.cpp (22910B)


      1 /*
      2   ZynAddSubFX - a software synthesizer
      3 
      4   EffectMgr.cpp - Effect manager, an interface between the program and effects
      5   Copyright (C) 2002-2005 Nasca Octavian Paul
      6   Author: Nasca Octavian Paul
      7 
      8   This program is free software; you can redistribute it and/or
      9   modify it under the terms of the GNU General Public License
     10   as published by the Free Software Foundation; either version 2
     11   of the License, or (at your option) any later version.
     12 */
     13 
     14 #include <rtosc/ports.h>
     15 #include <rtosc/port-sugar.h>
     16 #include <iostream>
     17 #include <cassert>
     18 
     19 #include "EffectMgr.h"
     20 #include "Effect.h"
     21 #include "Alienwah.h"
     22 #include "Reverb.h"
     23 #include "Echo.h"
     24 #include "Chorus.h"
     25 #include "Distortion.h"
     26 #include "EQ.h"
     27 #include "DynamicFilter.h"
     28 #include "Phaser.h"
     29 #include "Sympathetic.h"
     30 #include "../Effects/Reverse.h"
     31 #include "../Misc/XMLwrapper.h"
     32 #include "../Misc/Util.h"
     33 
     34 #include "../Params/FilterParams.h"
     35 #include "../Misc/Allocator.h"
     36 #include "../Misc/Time.h"
     37 
     38 namespace zyn {
     39 
     40 #define rObject EffectMgr
     41 #define rSubtype(name) \
     42     {STRINGIFY(name)"/", NULL, &name::ports,\
     43         [](const char *msg, rtosc::RtData &data){\
     44             rObject &o = *(rObject*)data.obj; \
     45             data.obj = dynamic_cast<name*>(o.efx); \
     46             if(!data.obj) \
     47                 return; \
     48             SNIP \
     49             name::ports.dispatch(msg, data); \
     50         }}
     51 static const rtosc::Ports local_ports = {
     52     rSelf(EffectMgr, rEnabledByCondition(self-enabled)),
     53     {"preset::i", rProp(parameter) rDepends(efftype) rDoc("Effect Preset Selector")
     54         rDefault(0), NULL,
     55         [](const char *msg, rtosc::RtData &d)
     56         {
     57             char loc[1024];
     58             EffectMgr *eff = (EffectMgr*)d.obj;
     59             if(!rtosc_narguments(msg))
     60                 d.reply(d.loc, "i", eff->getpreset());
     61             else {
     62                 eff->changepresetrt(rtosc_argument(msg, 0).i);
     63                 d.broadcast(d.loc, "i", eff->getpreset());
     64 
     65                 //update parameters as well
     66                 fast_strcpy(loc, d.loc, sizeof(loc));
     67                 char *tail = strrchr(loc, '/');
     68                 if(!tail)
     69                     return;
     70                 for(int i=0;i<128;++i) {
     71                     snprintf(tail+1, sizeof(loc)-(tail+1-loc), "parameter%d", i);
     72                     d.broadcast(loc, "i", eff->geteffectparrt(i));
     73                 }
     74             }
     75         }}, // must come before rPaste, because apropos otherwise picks "preset-type" first
     76     rPaste,
     77     rEnabledCondition(self-enabled, obj->geteffect()),
     78     rEnabledCondition(is-dynamic-filter, (obj->geteffect()==8)),
     79     rRecurp(filterpars, rDepends(preset), rEnabledByCondition(is-dynamic-filter), "Filter Parameter for Dynamic Filter"),
     80     {"Pvolume::i", rProp(parameter) rLinear(0,127) rShort("amt") rDoc("amount of effect"),
     81         0,
     82         [](const char *msg, rtosc::RtData &d)
     83         {
     84             EffectMgr *eff = (EffectMgr*)d.obj;
     85             if(!rtosc_narguments(msg))
     86                 d.reply(d.loc, "i", eff->geteffectparrt(0));
     87             else if(rtosc_type(msg, 0) == 'i'){
     88                 eff->seteffectparrt(0, rtosc_argument(msg, 0).i);
     89                 d.broadcast(d.loc, "i", eff->geteffectparrt(0));
     90             }
     91         }},
     92     {"Ppanning::i", rProp(parameter) rLinear(0,127) rShort("pan") rDoc("panning"),
     93         0,
     94         [](const char *msg, rtosc::RtData &d)
     95         {
     96             EffectMgr *eff = (EffectMgr*)d.obj;
     97             if(!rtosc_narguments(msg))
     98                 d.reply(d.loc, "i", eff->geteffectparrt(1));
     99             else if(rtosc_type(msg, 0) == 'i'){
    100                 eff->seteffectparrt(1, rtosc_argument(msg, 0).i);
    101                 d.broadcast(d.loc, "i", eff->geteffectparrt(1));
    102             }
    103         }},
    104     {"parameter#128::i:T:F", rProp(parameter) rProp(alias) rLinear(0,127) rDoc("Parameter Accessor"),
    105         NULL,
    106         [](const char *msg, rtosc::RtData &d)
    107         {
    108             EffectMgr *eff = (EffectMgr*)d.obj;
    109             const char *mm = msg;
    110             while(!isdigit(*mm))++mm;
    111 
    112             if(!rtosc_narguments(msg))
    113                 d.reply(d.loc, "i", eff->geteffectparrt(atoi(mm)));
    114             else if(rtosc_type(msg, 0) == 'i'){
    115                 eff->seteffectparrt(atoi(mm), rtosc_argument(msg, 0).i);
    116                 d.broadcast(d.loc, "i", eff->geteffectparrt(atoi(mm)));
    117             } else if(rtosc_type(msg, 0) == 'T'){
    118                 eff->seteffectparrt(atoi(mm), 127);
    119                 d.broadcast(d.loc, "i", eff->geteffectparrt(atoi(mm)));
    120             } else if(rtosc_type(msg, 0) == 'F'){
    121                 eff->seteffectparrt(atoi(mm), 0);
    122                 d.broadcast(d.loc, "i", eff->geteffectparrt(atoi(mm)));
    123             }
    124         }},
    125     {"numerator::i", rShort("num") rDefault(0) rLinear(0,99)
    126         rProp(parameter) rDoc("Numerator of ratio to bpm"), NULL,
    127         [](const char *msg, rtosc::RtData &d)
    128         {
    129             EffectMgr *eff = (EffectMgr*)d.obj;
    130             if(rtosc_narguments(msg)) {
    131                 int val = rtosc_argument(msg, 0).i;
    132                 if (val>=0) {
    133                     eff->numerator = val;
    134                     int Pdelay, Pfreq;
    135                     float freq, delay;
    136                     if (eff->numerator&&eff->denominator) {
    137                         eff->efx->speedfactor = (float)eff->denominator / (4.0f *(float)eff->numerator);
    138                         switch(eff->nefx) {
    139                         case 2: // Echo
    140                         case 10: // Reverse
    141                             // invert:
    142                             // delay = ((Pdelay+1)/128.0f*MAX_REV_DELAY_SECONDS); //0 .. x sec
    143                             // Pdelay = (delay * 128.0f / MAX_REV_DELAY_SECONDS) -1
    144                             // delay = 60 / tempo * 4 * numerator / denominator
    145                             assert(eff->time->tempo > 0);
    146                             delay = 60.0f / ((float)eff->time->tempo * eff->efx->speedfactor);
    147                             Pdelay = (unsigned char)(delay * 128.0f / MAX_REV_DELAY_SECONDS)-1;
    148                             eff->seteffectparrt(2, Pdelay);
    149                             break;
    150                         case 3: // Chorus
    151                         case 4: // Phaser
    152                         case 5: // Alienwah
    153                         case 8: // DynamicFilter
    154                             freq =  (float)eff->time->tempo * 60.0 * eff->efx->speedfactor;
    155                             // invert:
    156                             // (powf(2.0f, Pfreq / 127.0f * 10.0f) - 1.0f) * 0.03f
    157                             Pfreq = (int)roundf(logf((freq/0.03f)+1.0f)/LOG_2 * 12.7f);
    158                             eff->seteffectparrt(2, Pfreq);
    159                             break;
    160                         case 1: // Reverb
    161                         case 6: // Distortion
    162                         case 7: // EQ
    163                         default:
    164                             break;
    165                         }
    166                     }
    167                     else
    168                         eff->efx->speedfactor = 0.0f;
    169                 }
    170                 d.broadcast(d.loc, "i", val);
    171             } else {
    172                 d.reply(d.loc, "i", eff->numerator);
    173             }
    174         }},
    175     {"denominator::i", rShort("dem") rDefault(4) rLinear(1,99)
    176         rProp(parameter) rDoc("Denominator of ratio to bpm"), NULL,
    177         [](const char *msg, rtosc::RtData &d)
    178         {
    179             EffectMgr *eff = (EffectMgr*)d.obj;
    180             if(rtosc_narguments(msg)) {
    181                 int val = rtosc_argument(msg, 0).i;
    182                 if (val > 0) {
    183                     eff->denominator = val;
    184                     int Pdelay, Pfreq;
    185                     float freq, delay;
    186                     if (eff->numerator&&eff->denominator) {
    187                         eff->efx->speedfactor = (float)eff->denominator / (4.0f *(float)eff->numerator);
    188                         switch(eff->nefx) {
    189                         case 2: // Echo
    190                         case 10: // Reverse
    191                             assert(eff->time->tempo > 0);
    192                             delay = 60.0f / ((float)eff->time->tempo * eff->efx->speedfactor);
    193                             Pdelay = (unsigned char)(delay * 128.0f / MAX_REV_DELAY_SECONDS)-1;
    194                             eff->seteffectparrt(2, Pdelay);
    195                             break;
    196                         case 3: // Chorus
    197                         case 4: // Phaser
    198                         case 5: // Alienwah
    199                         case 8: // DynamicFilter
    200                             freq =  (float)eff->time->tempo * 60.0 * eff->efx->speedfactor;
    201                             // invert:
    202                             // (powf(2.0f, Pfreq / 127.0f * 10.0f) - 1.0f) * 0.03f
    203                             Pfreq = (int)roundf(logf((freq/0.03f)+1.0f)/LOG_2 * 12.7f);
    204                             eff->seteffectparrt(2, Pfreq);
    205                             break;
    206                         case 1: // Reverb
    207                         case 6: // Distortion
    208                         case 7: // EQ
    209                         default:
    210                             break;
    211                         }
    212                     }
    213                     else
    214                         eff->efx->speedfactor = 0.0f;
    215                 }
    216                 d.broadcast(d.loc, "i", val);
    217             } else {
    218                 d.reply(d.loc, "i", eff->denominator);
    219             }
    220         }},
    221     {"eq-coeffs:", rProp(internal) rDoc("Get equalizer Coefficients"), NULL,
    222         [](const char *, rtosc::RtData &d)
    223         {
    224             EffectMgr *eff = (EffectMgr*)d.obj;
    225             if(eff->nefx != 7)
    226                 return;
    227             EQ *eq = (EQ*)eff->efx;
    228             float a[MAX_EQ_BANDS*MAX_FILTER_STAGES*3];
    229             float b[MAX_EQ_BANDS*MAX_FILTER_STAGES*3];
    230             memset(a, 0, sizeof(a));
    231             memset(b, 0, sizeof(b));
    232             eq->getFilter(a,b);
    233             d.reply(d.loc, "bb", sizeof(a), a, sizeof(b), b);
    234         }},
    235     {"efftype::i:c:S", rOptions(Disabled, Reverb, Echo, Chorus,
    236      Phaser, Alienwah, Distortion, EQ, DynFilter, Sympathetic, Reverse) rDefault(Disabled)
    237      rProp(parameter) rDoc("Get Effect Type"), NULL,
    238      rCOptionCb(obj->nefx, obj->changeeffectrt(var))},
    239     {"efftype:b", rProp(internal) rDoc("Pointer swap EffectMgr"), NULL,
    240         [](const char *msg, rtosc::RtData &d)
    241         {
    242             printf("OBSOLETE METHOD CALLED\n");
    243             EffectMgr *eff  = (EffectMgr*)d.obj;
    244             EffectMgr *eff_ = *(EffectMgr**)rtosc_argument(msg,0).b.data;
    245 
    246             //Lets trade data
    247             std::swap(eff->nefx,eff_->nefx);
    248             std::swap(eff->efx,eff_->efx);
    249             std::swap(eff->filterpars,eff_->filterpars);
    250             std::swap(eff->efxoutl, eff_->efxoutl);
    251             std::swap(eff->efxoutr, eff_->efxoutr);
    252 
    253             //Return the old data for destruction
    254             d.reply("/free", "sb", "EffectMgr", sizeof(EffectMgr*), &eff_);
    255         }},
    256     rSubtype(Alienwah),
    257     rSubtype(Chorus),
    258     rSubtype(Distortion),
    259     rSubtype(DynamicFilter),
    260     rSubtype(Echo),
    261     rSubtype(EQ),
    262     rSubtype(Phaser),
    263     rSubtype(Reverb),
    264     rSubtype(Sympathetic),
    265     rSubtype(Reverse),
    266 };
    267 
    268 const rtosc::Ports &EffectMgr::ports = local_ports;
    269 
    270 EffectMgr::EffectMgr(Allocator &alloc, const SYNTH_T &synth_,
    271                      const bool insertion_, const AbsTime *time_, Sync *sync_)
    272     :insertion(insertion_),
    273       efxoutl(new float[synth_.buffersize]),
    274       efxoutr(new float[synth_.buffersize]),
    275       filterpars(new FilterParams(in_effect, time_)),
    276       nefx(0),
    277       efx(NULL),
    278       time(time_),
    279       sync(sync_),
    280       numerator(0),
    281       denominator(4),
    282       dryonly(false),
    283       memory(alloc),
    284       synth(synth_)
    285 {
    286     setpresettype("Peffect");
    287     memset(efxoutl, 0, synth.bufferbytes);
    288     memset(efxoutr, 0, synth.bufferbytes);
    289     memset(settings, 255, sizeof(settings));
    290     defaults();
    291 }
    292 
    293 
    294 EffectMgr::~EffectMgr()
    295 {
    296     if(sync) sync->detach(efx);
    297     memory.dealloc(efx);
    298     delete filterpars;
    299     delete [] efxoutl;
    300     delete [] efxoutr;
    301 }
    302 
    303 void EffectMgr::defaults(void)
    304 {
    305     changeeffect(0);
    306     setdryonly(false);
    307 }
    308 
    309 //Change the effect
    310 void EffectMgr::changeeffectrt(int _nefx, bool avoidSmash)
    311 {
    312     cleanup();
    313     if(nefx == _nefx && efx != NULL)
    314         return;
    315     nefx = _nefx;
    316     preset = 0;
    317     memset(efxoutl, 0, synth.bufferbytes);
    318     memset(efxoutr, 0, synth.bufferbytes);
    319     memory.dealloc(efx);
    320 
    321     int new_loc = (_nefx == 8) ? dynfilter_0 : in_effect;
    322     if(new_loc != filterpars->loc)
    323         filterpars->updateLoc(new_loc);
    324     EffectParams pars(memory, insertion, efxoutl, efxoutr, 0,
    325             synth.samplerate, synth.buffersize, filterpars, avoidSmash);
    326 
    327     try {
    328         switch (nefx) {
    329             case 1:
    330                 efx = memory.alloc<Reverb>(pars);
    331                 break;
    332             case 2:
    333                 efx = memory.alloc<Echo>(pars);
    334                 break;
    335             case 3:
    336                 efx = memory.alloc<Chorus>(pars);
    337                 break;
    338             case 4:
    339                 efx = memory.alloc<Phaser>(pars);
    340                 break;
    341             case 5:
    342                 efx = memory.alloc<Alienwah>(pars);
    343                 break;
    344             case 6:
    345                 efx = memory.alloc<Distortion>(pars);
    346                 break;
    347             case 7:
    348                 efx = memory.alloc<EQ>(pars);
    349                 break;
    350             case 8:
    351                 efx = memory.alloc<DynamicFilter>(pars);
    352                 break;
    353             case 9:
    354                 efx = memory.alloc<Sympathetic>(pars);
    355                 break;
    356             case 10:
    357                 efx = memory.alloc<Reverse>(pars, time);
    358                 if(sync) sync->attach(efx);
    359                 break;
    360             //put more effect here
    361             default:
    362                 efx = NULL;
    363                 break; //no effect (thru)
    364         }
    365 
    366         // set freq / delay params according to bpm ratio
    367         int Pdelay, Pfreq;
    368         float freq;
    369         if (numerator>0) {
    370             switch(nefx) {
    371                 case 2: // Echo
    372                 case 10:// Reverse
    373                     // invert:
    374                     // delay = (Pdelay / 127.0f * 1.5f); //0 .. 1.5 sec
    375                     Pdelay = (int)roundf((20320.0f / (float)time->tempo) *
    376                                          ((float)numerator / (float)denominator));
    377                     if (numerator&&denominator)
    378                         seteffectparrt(2, Pdelay);
    379                     break;
    380                 case 3: // Chorus
    381                 case 4: // Phaser
    382                 case 5: // Alienwah
    383                 case 8: // DynamicFilter
    384                     freq =  ((float)time->tempo *
    385                              (float)denominator /
    386                              (240.0f * (float)numerator));
    387                     // invert:
    388                     // (powf(2.0f, Pfreq / 127.0f * 10.0f) - 1.0f) * 0.03f
    389                     Pfreq = (int)roundf(logf((freq/0.03f)+1.0f)/LOG_2 * 12.7f);
    390                     if (numerator&&denominator)
    391                         seteffectparrt(2, Pfreq);
    392                     break;
    393                 case 1: // Reverb
    394                 case 6: // Distortion
    395                 case 7: // EQ
    396                 default:
    397                     break;
    398             }
    399         }
    400 
    401     } catch (std::bad_alloc &ba) {
    402         std::cerr << "failed to change effect " << _nefx << ": " << ba.what() << std::endl;
    403         return;
    404     }
    405 
    406     if(!avoidSmash)
    407         for(int i = 0; i != 128; i++)
    408             settings[i] = geteffectparrt(i);
    409 }
    410 
    411 void EffectMgr::changeeffect(int _nefx)
    412 {
    413     nefx = _nefx;
    414     //preset    = 0;
    415 }
    416 
    417 //Obtain the effect number
    418 int EffectMgr::geteffect(void)
    419 {
    420     return nefx;
    421 }
    422 
    423 void EffectMgr::changesettingsrt(const short int *p_value)
    424 {
    425     for(int i = 0; i != 128; i++) {
    426         short int value = p_value[i];
    427         /* check if setting is missing */
    428         if(value == -1) {
    429             if(efx)
    430                 value = efx->getpresetpar(preset, i);
    431             else
    432                 value = 0;
    433         }
    434         /* update settings */
    435         seteffectparrt(i, value);
    436     }
    437 }
    438 
    439 // Initialize An Effect in RT context
    440 void EffectMgr::init(void)
    441 {
    442     kill();
    443     changeeffectrt(nefx, true);
    444     changepresetrt(preset, true);
    445     changesettingsrt(settings);
    446 }
    447 
    448 //Strip effect manager of it's realtime memory
    449 void EffectMgr::kill(void)
    450 {
    451     //printf("Killing Effect(%d)\n", nefx);
    452     if(sync) sync->detach(efx);
    453     memory.dealloc(efx);
    454 }
    455 
    456 // Cleanup the current effect
    457 void EffectMgr::cleanup(void)
    458 {
    459     if(efx)
    460         efx->cleanup();
    461 }
    462 
    463 
    464 // Get the preset of the current effect
    465 unsigned char EffectMgr::getpreset(void)
    466 {
    467     if(efx)
    468         return efx->Ppreset;
    469     else
    470         return 0;
    471 }
    472 
    473 // Change the preset of the current effect
    474 void EffectMgr::changepreset(unsigned char npreset)
    475 {
    476     preset = npreset;
    477 }
    478 
    479 // Change the preset of the current effect
    480 void EffectMgr::changepresetrt(unsigned char npreset, bool avoidSmash)
    481 {
    482     preset = npreset;
    483     if(avoidSmash && dynamic_cast<DynamicFilter*>(efx)) {
    484         efx->Ppreset = npreset;
    485         return;
    486     }
    487     if(efx)
    488         efx->setpreset(npreset);
    489     if(!avoidSmash)
    490         for(int i = 0; i != 128; i++)
    491             settings[i] = geteffectparrt(i);
    492 }
    493 
    494 //Change a parameter of the current effect
    495 void EffectMgr::seteffectparrt(int npar, unsigned char value)
    496 {
    497     if(npar < 0 || npar >= 128)
    498         return;
    499     settings[npar] = value;
    500 
    501     if(!efx)
    502         return;
    503     try {
    504         efx->changepar(npar, value);
    505     } catch (std::bad_alloc &ba) {
    506         std::cerr << "failed to change effect parameter " << npar << " to " << value << ": " << ba.what() << std::endl;
    507     }
    508 }
    509 
    510 unsigned char EffectMgr::geteffectparrt(int npar)
    511 {
    512     if(!efx)
    513         return 0;
    514     return efx->getpar(npar);
    515 }
    516 
    517 // Apply the effect
    518 void EffectMgr::out(float *smpsl, float *smpsr)
    519 {
    520     if(!efx) {
    521         if(!insertion)
    522             for(int i = 0; i < synth.buffersize; ++i) {
    523                 smpsl[i]   = 0.0f;
    524                 smpsr[i]   = 0.0f;
    525                 efxoutl[i] = 0.0f;
    526                 efxoutr[i] = 0.0f;
    527             }
    528         return;
    529     }
    530     for(int i = 0; i < synth.buffersize; ++i) {
    531         smpsl[i]  += synth.denormalkillbuf[i];
    532         smpsr[i]  += synth.denormalkillbuf[i];
    533         efxoutl[i] = 0.0f;
    534         efxoutr[i] = 0.0f;
    535     }
    536     efx->out(smpsl, smpsr);
    537 
    538     float volume = efx->volume;
    539 
    540     if(nefx == 7) { //this is need only for the EQ effect
    541         memcpy(smpsl, efxoutl, synth.bufferbytes);
    542         memcpy(smpsr, efxoutr, synth.bufferbytes);
    543         return;
    544     }
    545 
    546     //Insertion effect
    547     if(insertion != 0) {
    548         float v1, v2;
    549         if(volume < 0.5f) {
    550             v1 = 1.0f;
    551             v2 = volume * 2.0f;
    552         }
    553         else {
    554             v1 = (1.0f - volume) * 2.0f;
    555             v2 = 1.0f;
    556         }
    557         if((nefx == 1) || (nefx == 2))
    558             v2 *= v2;  //for Reverb and Echo, the wet function is not liniar
    559 
    560         if(dryonly)   //this is used for instrument effect only
    561             for(int i = 0; i < synth.buffersize; ++i) {
    562                 smpsl[i]   *= v1;
    563                 smpsr[i]   *= v1;
    564                 efxoutl[i] *= v2;
    565                 efxoutr[i] *= v2;
    566             }
    567         else // normal instrument/insertion effect
    568             for(int i = 0; i < synth.buffersize; ++i) {
    569                 smpsl[i] = smpsl[i] * v1 + efxoutl[i] * v2;
    570                 smpsr[i] = smpsr[i] * v1 + efxoutr[i] * v2;
    571             }
    572     }
    573     else // System effect
    574         for(int i = 0; i < synth.buffersize; ++i) {
    575             efxoutl[i] *= 2.0f * volume;
    576             efxoutr[i] *= 2.0f * volume;
    577             smpsl[i]    = efxoutl[i];
    578             smpsr[i]    = efxoutr[i];
    579         }
    580 }
    581 
    582 
    583 // Get the effect volume for the system effect
    584 float EffectMgr::sysefxgetvolume(void)
    585 {
    586     return efx ? efx->outvolume : 1.0f;
    587 }
    588 
    589 
    590 // Get the EQ response
    591 float EffectMgr::getEQfreqresponse(float freq)
    592 {
    593     return (nefx == 7) ? efx->getfreqresponse(freq) : 0.0f;
    594 }
    595 
    596 
    597 void EffectMgr::setdryonly(bool value)
    598 {
    599     dryonly = value;
    600 }
    601 
    602 void EffectMgr::paste(EffectMgr &e)
    603 {
    604     changeeffectrt(e.nefx, true);
    605     changepresetrt(e.preset, true);
    606     changesettingsrt(e.settings);
    607     if(dynamic_cast<DynamicFilter*>(efx)) {
    608         std::swap(filterpars, e.filterpars);
    609         efx->filterpars = filterpars;
    610     }
    611     cleanup(); // cleanup the effect and recompute its parameters
    612 }
    613 
    614 void EffectMgr::add2XML(XMLwrapper& xml)
    615 {
    616     xml.addpar("type", geteffect());
    617 
    618     if(!geteffect())
    619         return;
    620     xml.addpar("preset", preset);
    621 
    622     xml.beginbranch("EFFECT_PARAMETERS");
    623     for(int n = 0; n != 128; n++) {
    624         int par;
    625         int def;
    626         if(efx) {
    627             par = efx->getpar(n);
    628             def = efx->getpresetpar(preset, n);
    629         } else {
    630             par = settings[n];
    631             def = -1;
    632         }
    633         /* don't store default values */
    634         if(par == def)
    635             continue;
    636         xml.beginbranch("par_no", n);
    637         xml.addpar("par", par);
    638         xml.endbranch();
    639     }
    640     assert(filterpars);
    641     if(nefx == 8) {
    642         xml.beginbranch("FILTER");
    643         filterpars->add2XML(xml);
    644         xml.endbranch();
    645     }
    646     xml.endbranch();
    647     xml.addpar("numerator", numerator);
    648     xml.addpar("denominator", denominator);
    649 }
    650 
    651 void EffectMgr::getfromXML(XMLwrapper& xml)
    652 {
    653     changeeffect(xml.getpar127("type", geteffect()));
    654 
    655     if(!geteffect())
    656         return;
    657 
    658     preset = xml.getpar127("preset", preset);
    659 
    660     if(xml.enterbranch("EFFECT_PARAMETERS")) {
    661         for(int n = 0; n != 128; n++) {
    662             if(xml.enterbranch("par_no", n) == 0) {
    663                 /*
    664                  * XXX workaround for old presets:
    665                  *
    666                  * All effect parameters have a default value.
    667                  * Default values are skipped when storing parameters,
    668                  * and must appear as the default value when loading.
    669                  *
    670                  * Up until recently it was assumed that the default
    671                  * value of all parameters is zero. This is no longer
    672                  * true, but when loading old presets we need to
    673                  * preserve this behaviour! Else sounds may change.
    674                  */
    675                 if (xml.fileversion() < version_type(3,0,6) &&
    676                     /* XXX old presets don't have DC offset */
    677                     (geteffect() != 6 || n < 11)) {
    678                         settings[n] = 0;
    679                 } else {
    680                         settings[n] = -1; /* use parameter default */
    681                 }
    682             } else {
    683                 settings[n] = xml.getpar127("par", 0);
    684                 xml.exitbranch();
    685             }
    686         }
    687         assert(filterpars);
    688         if(xml.enterbranch("FILTER")) {
    689             filterpars->getfromXML(xml);
    690             xml.exitbranch();
    691         }
    692         xml.exitbranch();
    693     }
    694     numerator = xml.getpar("numerator", numerator, 0, 99);
    695     denominator = xml.getpar("denominator", denominator, 1, 99);
    696     cleanup();
    697 }
    698 
    699 }