zynaddsubfx

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

FilterParams.cpp (28325B)


      1 /*
      2   ZynAddSubFX - a software synthesizer
      3 
      4   FilterParams.cpp - Parameters for filter
      5   Copyright (C) 2002-2005 Nasca Octavian Paul
      6   Copyright (C) 2017      Mark McCurry
      7   Author: Nasca Octavian Paul
      8           Mark McCurry
      9 
     10   This program is free software; you can redistribute it and/or
     11   modify it under the terms of the GNU General Public License
     12   as published by the Free Software Foundation; either version 2
     13   of the License, or (at your option) any later version.
     14 */
     15 
     16 #include "FilterParams.h"
     17 #include "../Misc/Util.h"
     18 #include "../Misc/Time.h"
     19 #include "../DSP/AnalogFilter.h"
     20 #include "../DSP/SVFilter.h"
     21 #include <cmath>
     22 #include <cstdio>
     23 #include <cstdlib>
     24 
     25 #include <rtosc/rtosc.h>
     26 #include <rtosc/ports.h>
     27 #include <rtosc/port-sugar.h>
     28 using namespace rtosc;
     29 
     30 namespace zyn {
     31 
     32 // g++ 4.8 needs this variable saved separately, otherwise it segfaults
     33 constexpr int sizeof_pvowels = sizeof(FilterParams::Pvowels);
     34 
     35 #define rObject FilterParams::Pvowels_t::formants_t
     36 
     37 static const rtosc::Ports subsubports = {
     38     rParamZyn(loc, rProp(internal), "location of the filter"),
     39     rParamZyn(freq, rShort("f.freq"), rDefault(128),
     40               rDefaultDepends(loc),
     41               rPresets(34, 99, 108, 61, 71, 99, 70, 80, 20, 100),
     42               "Formant frequency"),
     43     rParamZyn(amp,  rShort("f.str"),  rDefault(127),
     44               rDefaultDepends(loc),
     45               rPresets(127, 122, 112, 127, 121, 117, 127, 122, 127, 121),
     46               "Strength of formant"),
     47     rParamZyn(q,    rShort("f.q"),    rDefault(64),
     48               "The formant's quality factor, also known as "
     49               "resonance bandwidth or Q for short"),
     50 };
     51 #undef rObject
     52 
     53 static const rtosc::Ports subports = {
     54     {"Pformants#" STRINGIFY(FF_MAX_FORMANTS) "/", NULL, &subsubports,
     55         [](const char *msg, RtData &d) {
     56             const char *mm = msg;
     57             while(*mm && !isdigit(*mm)) ++mm;
     58             unsigned idx = atoi(mm);
     59 
     60             SNIP;
     61             FilterParams::Pvowels_t *obj = (FilterParams::Pvowels_t *) d.obj;
     62             d.obj = (void*) &obj->formants[idx];
     63             subsubports.dispatch(msg, d);
     64         }},
     65 };
     66 
     67 
     68 #define rObject FilterParams
     69 #undef  rChangeCb
     70 #define rChangeCb do { obj->changed = true; if ( obj->time) {      \
     71     obj->last_update_timestamp = obj->time->time(); } } while(false)
     72 const rtosc::Ports FilterParams::ports = {
     73     rSelf(FilterParams),
     74     rPasteRt,
     75     rArrayPasteRt,
     76     rOption(loc, rProp(internal),
     77             rOptions(ad_global_filter, ad_voice_filter, sub_filter, in_effect,
     78                      dynfilter_0, dynfilter_1, dynfilter_2,
     79                      dynfilter_3, dynfilter_4),
     80             "location of the filter"),
     81     rOption(Pcategory,          rShort("class"),
     82             rOptions(analog, formant, st.var., moog, comb),
     83             rDefault(analog), rDefaultDepends(loc),
     84             rPreset(dynfilter_1, 2), rPreset(dynfilter_3, formant), rPreset(dynfilter_4, formant),
     85             "Class of filter"),
     86     rOption(Ptype,              rShort("type"),
     87             rOptions(LP1, HP1, LP2, HP2, BP, notch, peak, l.shelf, h.shelf),
     88             rDefault(LP2), rDefaultDepends(loc),
     89             rPreset(dynfilter_0, LP2),
     90             rPreset(dynfilter_1, LP1),
     91             rPreset(dynfilter_2, BP),
     92             rPreset(dynfilter_3, LP1),
     93             rPreset(dynfilter_4, LP1),
     94             "Filter Type"),
     95     rParamI(Pstages,            rShort("stages"),
     96             rLinear(0,5), rDefault(0), rDefaultDepends(loc),
     97             rPreset(dynfilter_0, 1), rPreset(dynfilter_2, 2),
     98             rPreset(dynfilter_3, 1), rPreset(dynfilter_4, 1),
     99             "Filter Stages"),
    100     rParamF(baseq,               rShort("q"),      rUnit(none),  rLog(0.1, 1000),
    101             rDefaultDepends(loc),
    102             rPreset(ad_global_filter, 0x1.1592acp+0),
    103             rPreset(ad_voice_filter, 0x1.e2f3ap+1),
    104             rPreset(sub_filter, 0x1.1592acp+0),
    105             rPreset(in_effect, 0x1.384298p+2),
    106             rPreset(dynfilter_0, 0x1.384298p+2),
    107             rPreset(dynfilter_1, 0x1.384298p+2),
    108             rPreset(dynfilter_2, 0x1.384298p+2),
    109             rPreset(dynfilter_3, 0x1.d04b16p+2),
    110             rPreset(dynfilter_4, 0x1.d04b16p+2),
    111             "Quality Factor (resonance/bandwidth)"),
    112     rParamF(basefreq,           rShort("cutoff"),
    113             rUnit(Hz),    rLog(31.25, 32000),
    114             rDefaultDepends(loc),
    115             rPreset(ad_global_filter, 30313.21f),
    116             rPreset(ad_voice_filter, 30313.21f),
    117             rPreset(sub_filter, 30313.21f),
    118             rPreset(in_effect, 0x1.f3fffcp+9),
    119             rPreset(dynfilter_0, 0x1.65673ep+8),
    120             rPreset(dynfilter_1, 0x1.818d7ap+10),
    121             rPreset(dynfilter_2, 0x1.f3fffcp+9),
    122             rPreset(dynfilter_3, 0x1.d48ab6p+8),
    123             rPreset(dynfilter_4, 0x1.f3fffcp+9),
    124             "Base cutoff frequency"),
    125     rParamF(freqtracking,       rShort("f.track"), rUnit(%),
    126             rLinear(-100, 100), rDefault(0.0f),
    127             "Frequency Tracking amount"),
    128     rParamF(gain,               rShort("gain"),    rUnit(dB),
    129             rLinear(-30, 30),   rDefault(0.0f),
    130             "Output Gain"),
    131     rParamI(Pnumformants,       rShort("formants"),
    132             rLinear(1,12),      rDefault(3),         rDefaultDepends(loc),
    133             rPreset(dynfilter_4, 2),
    134             "Number of formants to be used"),
    135     rParamZyn(Pformantslowness, rShort("slew"),
    136             rDefault(64), "Rate that formants change"),
    137     rParamZyn(Pvowelclearness,  rShort("clarity"), rDefault(64),
    138             rDefaultDepends(loc), rPreset(dynfilter_4, 0),
    139             "How much each vowel is smudged with the next in sequence. A high clarity will avoid smudging."),
    140     rParamZyn(Pcenterfreq,      rShort("cutoff"),  rDefault(64),
    141             "Center Freq (formant)"),
    142     rParamZyn(Poctavesfreq,     rShort("octaves"), rDefault(64),
    143             "Number of octaves for formant"),
    144 
    145     rParamI(Psequencesize,    rShort("seq.size"),
    146             rLinear(0, FF_MAX_SEQUENCE),
    147             rDefault(3), rDefaultDepends(loc),
    148             rPreset(dynfilter_3, 2), rPreset(dynfilter_4, 2),
    149             "Length of vowel sequence"),
    150     rParamZyn(Psequencestretch, rShort("seq.str"),
    151             rDefault(40), "How modulators stretch the sequence"),
    152     rToggle(Psequencereversed,  rShort("reverse"),
    153             rDefault(false), "If the modulator input is inverted"),
    154 
    155     {"vowel_seq#" STRINGIFY(FF_MAX_SEQUENCE) "::i", rShort("vowel") rProp(parameter)
    156         rDefault([0 1 2 3 4 5 0 1])
    157         rDoc("Vowel number of this sequence position"), NULL,
    158         [](const char *msg, RtData &d){
    159             FilterParams *obj = (FilterParams *) d.obj;
    160             const char *mm = msg;
    161             while(*mm && !isdigit(*mm)) ++mm;
    162             unsigned idx = atoi(mm);
    163             if(rtosc_narguments(msg)) {
    164                 obj->Psequence[idx].nvowel = rtosc_argument(msg, 0).i;
    165                 d.broadcast(d.loc, "i", obj->Psequence[idx].nvowel);
    166             } else
    167                 d.reply(d.loc, "i", obj->Psequence[idx].nvowel);
    168         }},
    169     {"type-svf::i:c:S", rProp(parameter) rProp(enumerated) rShort("type")
    170         rOptions(low, high, band, notch)
    171             rDoc("Filter Type"), 0, rOptionCb(Ptype)},
    172     {"type-moog::i:c:S", rProp(parameter) rProp(enumerated) rShort("type")
    173         rOptions(HP, BP, LP)
    174             rDoc("Filter Type"), 0, rOptionCb(Ptype)},
    175     {"type-comb::i:c:S", rProp(parameter) rProp(enumerated) rShort("type")
    176         rOptions(BWD, FWD, both, BWDN, FWDN, bothN)
    177             rDoc("Comb Filter Type"), 0, rOptionCb(Ptype)},
    178     //UI reader
    179     {"Pvowels:", rDoc("Get Formant Vowels"), NULL,
    180         [](const char *, RtData &d) {
    181             FilterParams *obj = (FilterParams *) d.obj;
    182             d.reply(d.loc, "b", sizeof_pvowels, obj->Pvowels);
    183         }},
    184 
    185     rEnabledCondition(is_formant_filter, obj->Pcategory == 1),
    186     {"Pvowels#" STRINGIFY(FF_MAX_VOWELS) "/",
    187         rEnabledByCondition(is_formant_filter),
    188         &subports,
    189         [](const char *msg, RtData &d) {
    190             const char *mm = msg;
    191             while(*mm && !isdigit(*mm)) ++mm;
    192             unsigned idx = atoi(mm);
    193 
    194             SNIP;
    195             FilterParams *obj = (FilterParams *) d.obj;
    196             d.obj = (void*)&obj->Pvowels[idx];
    197             subports.dispatch(msg, d);
    198 
    199             if(rtosc_narguments(msg))
    200                 rChangeCb;
    201         }},
    202     {"centerfreq:", rDoc("Get the center frequency of the formant's graph"),
    203         NULL, [](const char *, RtData &d) {
    204             FilterParams *obj = (FilterParams *) d.obj;
    205             d.reply(d.loc, "f", obj->getcenterfreq());
    206         }},
    207     {"octavesfreq:",
    208         rDoc("Get the number of octave that the formant functions applies to"),
    209         NULL, [](const char *, RtData &d) {
    210             FilterParams *obj = (FilterParams *) d.obj;
    211             d.reply(d.loc, "f", obj->getoctavesfreq());
    212         }},
    213     {"q_value:",
    214         rDoc("Q value for UI Response Graphs"),
    215         NULL, [](const char *, RtData &d) {
    216             FilterParams *obj = (FilterParams *) d.obj;
    217             d.reply(d.loc, "f", obj->getq());
    218         }},
    219     {"response:",
    220         rDoc("Get a frequency response"),
    221         NULL, [](const char *, RtData &d) {
    222             FilterParams *obj = (FilterParams *) d.obj;
    223             if(obj->Pcategory == 0) {
    224                 int order = 0;
    225                 float gain = dB2rap(obj->getgain());
    226                 if(obj->Ptype != 6 && obj->Ptype != 7 && obj->Ptype != 8)
    227                     gain = 1.0;
    228                 auto cf = AnalogFilter::computeCoeff(obj->Ptype,
    229                         Filter::getrealfreq(obj->getfreq()),
    230                         obj->getq(), obj->Pstages,
    231                         gain, 48000, order);
    232                 if(order == 2) {
    233                     d.reply(d.loc, "fffffff",
    234                             (float)obj->Pstages,
    235                             cf.c[0], cf.c[1], cf.c[2],
    236                             0.0,     cf.d[1], cf.d[2]);
    237                 } else if(order == 1) {
    238                     d.reply(d.loc, "fffff",
    239                             (float)obj->Pstages,
    240                             cf.c[0], cf.c[1],
    241                             0.0,     cf.d[1]);
    242                 }
    243             } else if(obj->Pcategory == 2) {
    244                 float gain = dB2rap(obj->getgain());
    245                 auto cf = SVFilter::computeResponse(obj->Ptype,
    246                         Filter::getrealfreq(obj->getfreq()),
    247                         obj->getq(), obj->Pstages,
    248                         gain, 48000);
    249                 d.reply(d.loc, "fffffff",
    250                         (float)obj->Pstages,
    251                         cf.b[0], cf.b[1], cf.b[2],
    252                         0.0,     -cf.a[1], -cf.a[2]);
    253             } else if(obj->Pcategory == 3) {
    254                 int order = 0;
    255                 float gain = dB2rap(obj->getgain());
    256                 if(obj->Ptype != 6 && obj->Ptype != 7 && obj->Ptype != 8)
    257                     gain = 1.0;
    258                 int tmp = 4-obj->Ptype;
    259                 if(tmp < 0 || tmp > 8)
    260                     return;
    261                 auto cf = AnalogFilter::computeCoeff(4-obj->Ptype,
    262                         Filter::getrealfreq(obj->getfreq()),
    263                         obj->getq(), obj->Pstages,
    264                         gain, 48000, order);
    265                 d.reply(d.loc, "fffffff",
    266                         (float)obj->Pstages,
    267                         cf.c[0], cf.c[1], cf.c[2],
    268                         0.0,     cf.d[1], cf.d[2]);
    269             }
    270         }},
    271     //    "", NULL, [](){}},"/freq"
    272     //{"Pvowels#" FF_MAX_VOWELS "/formants#" FF_MAX_FORMANTS "/amp",
    273     //    "", NULL, [](){}},
    274     //{"Pvowels#" FF_MAX_VOWELS "/formants#" FF_MAX_FORMANTS "/q",
    275     //    "", NULL, [](){}},
    276     //
    277         //struct Pvowels_t {
    278         //    struct formants_t {
    279         //        unsigned char freq, amp, q; //frequency,amplitude,Q
    280         //    } formants[FF_MAX_FORMANTS];
    281         //} Pvowels[FF_MAX_VOWELS];
    282     {"vowels:",
    283         rDoc("Get info for formant graph"),
    284         NULL, [](const char *, RtData &d) {
    285             FilterParams *obj = (FilterParams *) d.obj;
    286 
    287             rtosc_arg_t args[2+3*FF_MAX_FORMANTS*FF_MAX_VOWELS];
    288             char type[2+3*FF_MAX_FORMANTS*FF_MAX_VOWELS + 1] = {};
    289 
    290             type[0] = 'i';
    291             type[1] = 'i';
    292 
    293             args[0].i = FF_MAX_VOWELS;
    294             args[1].i = FF_MAX_FORMANTS;
    295 
    296 
    297             for(int i=0; i<FF_MAX_VOWELS; ++i) {
    298                 auto &val = obj->Pvowels[i];
    299                 for(int j=0; j<FF_MAX_FORMANTS; ++j) {
    300                     auto &f = val.formants[j];
    301                     //each formant is 3 arguments
    302                     //each vowel is FF_MAX_FORMANTS * length of formants long
    303                     auto *a = args + i*FF_MAX_FORMANTS*3 + j*3 + 2;
    304                     auto *t = type + i*FF_MAX_FORMANTS*3 + j*3 + 2;
    305                     a[0].f = obj->getformantfreq(f.freq);
    306                     a[1].f = obj->getformantamp(f.amp);
    307                     a[2].f = obj->getformantq(f.q);
    308                     //printf("<%d,%d,%d,%d,%d,%f,%f,%f>\n", i, j, f.freq, f.amp, f.q, a[0].f, a[1].f, a[2].f);
    309                     t[0] = t[1] = t[2] = 'f';
    310                 }
    311             }
    312             d.replyArray(d.loc, type, args);
    313         }},
    314 
    315     //Old 0..127 parameter mappings
    316     {"Pfreq::i",  rLinear(0, 127) rShort("cutoff")  rProp(deprecated)  rDoc("Center Freq"), 0,
    317         [](const char *msg, RtData &d) {
    318             FilterParams *obj = (FilterParams*)d.obj;
    319             if(rtosc_narguments(msg)) {
    320                 int Pfreq = rtosc_argument(msg, 0).i;
    321                 obj->basefreq  = basefreqFromOldPreq(Pfreq);
    322                 rChangeCb;
    323                 d.broadcast(d.loc, "i", Pfreq);
    324             } else {
    325                 float tmp = obj->basefreq;
    326                 tmp = log2f(tmp) - 9.96578428f;
    327                 tmp = (tmp / 5.0 + 1.0) * 64.0f;
    328                 int Pfreq = roundf(tmp);
    329                 d.reply(d.loc, "i", Pfreq);
    330             }
    331         }},
    332     {"Pfreqtrack::i", rLinear(0, 127) rShort("f.track") rProp(deprecated) rDoc("Frequency Tracking amount"), 0,
    333         [](const char *msg, RtData &d) {
    334             FilterParams *obj = (FilterParams*)d.obj;
    335             if(rtosc_narguments(msg)) {
    336                 int Pfreqtracking = rtosc_argument(msg, 0).i;
    337                 obj->freqtracking = 100.0f * (Pfreqtracking - 64.0f) / (64.0f);
    338                 rChangeCb;
    339                 d.broadcast(d.loc, "i", Pfreqtracking);
    340             } else {
    341                 int Pfreqtracking = static_cast<int>(roundf(((obj->freqtracking/100.0f) * 64.0f + 64.0f)));
    342                 d.reply(d.loc, "i", Pfreqtracking);
    343             }
    344         }},
    345     {"Pgain::i", rLinear(0, 127) rShort("gain") rProp(deprecated) rDoc("Output Gain"), 0,
    346         [](const char *msg, RtData &d) {
    347             FilterParams *obj = (FilterParams*)d.obj;
    348             if(rtosc_narguments(msg)) {
    349                 int Pgain = rtosc_argument(msg, 0).i;
    350                 obj->gain   = gainFromOldPgain(Pgain); //-30..30dB
    351                 rChangeCb;
    352                 d.broadcast(d.loc, "i", Pgain);
    353             } else {
    354                 int Pgain = roundf((obj->gain/30.0f + 1.0f) * 64.0f);
    355                 d.reply(d.loc, "i", Pgain);
    356             }
    357         }},
    358     {"Pq::i", rLinear(0,127) rShort("q") rProp(deprecated)
    359         rDoc("Quality Factor (resonance/bandwidth)"), 0,
    360         [](const char *msg, RtData &d) {
    361             FilterParams *obj = (FilterParams*)d.obj;
    362             if(rtosc_narguments(msg)) {
    363                 int Pq = rtosc_argument(msg, 0).i;
    364                 obj->baseq  = baseqFromOldPq(Pq);
    365                 rChangeCb;
    366                 d.broadcast(d.loc, "i", Pq);
    367             } else {
    368                 int Pq = roundf(127.0f * sqrtf(logf(0.9f + obj->baseq)/logf(1000.0f)));
    369                 d.reply(d.loc, "i", Pq);
    370             }
    371         }},
    372 };
    373 #undef rChangeCb
    374 #define rChangeCb
    375 
    376 
    377 
    378 void FilterParams::setup()
    379 {
    380     setpresettype("Pfilter");
    381 
    382     changed = false;
    383     defaults();
    384 }
    385 
    386 FilterParams::FilterParams(const AbsTime *time_)
    387     :FilterParams(in_effect, time_)
    388 {
    389 }
    390 
    391 FilterParams::FilterParams(unsigned char Ptype_,
    392                            unsigned char Pfreq_,
    393                            unsigned char Pq_,
    394                            consumer_location_t loc,
    395                            const AbsTime *time_):
    396         loc(loc), time(time_), last_update_timestamp(0),
    397         Dtype(Ptype_), Dfreq(Pfreq_), Dq(Pq_)
    398 {
    399     setup();
    400 }
    401 
    402 FilterParams::FilterParams(consumer_location_t loc,
    403                            const AbsTime *time_):
    404         loc(loc), time(time_), last_update_timestamp(0)
    405 {
    406     auto init =
    407         [&](unsigned char Ptype_, unsigned char Pfreq_, unsigned char Pq_)
    408     {
    409         Dtype = Ptype_;
    410         Dfreq = Pfreq_;
    411         Dq    = Pq_;
    412     };
    413 
    414     switch(loc)
    415     {
    416         case ad_global_filter:  init(2, 127, 40); break;
    417         case ad_voice_filter:   init(2, 127, 60); break;
    418         case sub_filter:        init(2, 127, 40); break;
    419         case in_effect:         init(0, 64, 64); break;
    420         default: throw std::logic_error("Invalid filter consumer location");
    421     }
    422 
    423     setup();
    424 }
    425 
    426 FilterParams::~FilterParams()
    427 {}
    428 
    429 
    430 void FilterParams::defaults()
    431 {
    432     Ptype = Dtype;
    433 
    434     Pstages       = 0;
    435     basefreq  = (Dfreq / 64.0f - 1.0f) * 5.0f;
    436     basefreq  = powf(2.0f, basefreq + 9.96578428f);
    437     baseq     = expf(powf((float) Dq / 127.0f, 2) * logf(1000.0f)) - 0.9f;
    438 
    439     gain = 0.0f;
    440     freqtracking = 0.0f;
    441 
    442     Pcategory     = 0;
    443 
    444     Pnumformants     = 3;
    445     Pformantslowness = 64;
    446     for(int j = 0; j < FF_MAX_VOWELS; ++j)
    447         defaults(j);
    448 
    449 
    450     Psequencesize = 3;
    451     for(int i = 0; i < FF_MAX_SEQUENCE; ++i)
    452         Psequence[i].nvowel = i % FF_MAX_VOWELS;
    453 
    454     Psequencestretch  = 40;
    455     Psequencereversed = 0;
    456     Pcenterfreq     = 64; //1 kHz
    457     Poctavesfreq    = 64;
    458     Pvowelclearness = 64;
    459 }
    460 
    461 void FilterParams::defaults(int n)
    462 {
    463     int j = n;
    464 
    465     updateLoc(loc, n);
    466     for(int i = 0; i < FF_MAX_FORMANTS; ++i) {
    467         Pvowels[j].formants[i].freq = (int)(RND * 127.0f); //some random freqs
    468         Pvowels[j].formants[i].q    = 64;
    469         Pvowels[j].formants[i].amp  = 127;
    470     }
    471 }
    472 
    473 void FilterParams::updateLoc(int newloc)
    474 {
    475     loc = newloc;
    476     for(int j = 0; j < FF_MAX_VOWELS; ++j)
    477         updateLoc(newloc, j);
    478 }
    479 
    480 void FilterParams::updateLoc(int newloc, int n)
    481 {
    482     int j   = n;
    483     for(int i = 0; i < FF_MAX_FORMANTS; ++i)
    484     {
    485         if (newloc == dynfilter_3 && i < 2 && j < 3)
    486         {
    487             Pvowels[j].formants[i].loc  = j * 3 + i; /* 0 .. 5 */
    488         } else if (newloc == dynfilter_4 && i < 2 && j < 2) {
    489             Pvowels[j].formants[i].loc  = j * 3 + i; /* 6 .. 9 */
    490         } else {
    491             Pvowels[j].formants[i].loc = -1;
    492         }
    493     }
    494 }
    495 
    496 
    497 /*
    498  * Get the parameters from other FilterParams
    499  */
    500 // WARNING! Function unused since 2004, see declaration in header
    501 void FilterParams::getfromFilterParams(const FilterParams *pars)
    502 {
    503     defaults();
    504 
    505     if(pars == NULL)
    506         return;
    507 
    508     Ptype = pars->Ptype;
    509 
    510     Pstages       = pars->Pstages;
    511     freqtracking  = pars->freqtracking;
    512     gain          = pars->gain;
    513     Pcategory     = pars->Pcategory;
    514 
    515     Pnumformants     = pars->Pnumformants;
    516     Pformantslowness = pars->Pformantslowness;
    517     for(int j = 0; j < FF_MAX_VOWELS; ++j)
    518         for(int i = 0; i < FF_MAX_FORMANTS; ++i) {
    519             Pvowels[j].formants[i].freq = pars->Pvowels[j].formants[i].freq;
    520             Pvowels[j].formants[i].q    = pars->Pvowels[j].formants[i].q;
    521             Pvowels[j].formants[i].amp  = pars->Pvowels[j].formants[i].amp;
    522         }
    523 
    524     Psequencesize = pars->Psequencesize;
    525     for(int i = 0; i < FF_MAX_SEQUENCE; ++i)
    526         Psequence[i].nvowel = pars->Psequence[i].nvowel;
    527 
    528     Psequencestretch  = pars->Psequencestretch;
    529     Psequencereversed = pars->Psequencereversed;
    530     Pcenterfreq     = pars->Pcenterfreq;
    531     Poctavesfreq    = pars->Poctavesfreq;
    532     Pvowelclearness = pars->Pvowelclearness;
    533 }
    534 
    535 
    536 /*
    537  * Parameter control
    538  */
    539 float FilterParams::getfreq() const
    540 {
    541     return log2(basefreq) - log2f(1000.0f);
    542 }
    543 
    544 float FilterParams::getq() const
    545 {
    546     return baseq;
    547 }
    548 float FilterParams::getfreqtracking(float notefreq) const
    549 {
    550     return log2f(notefreq / 440.0f) * (freqtracking / 100.0f);
    551 }
    552 
    553 float FilterParams::getgain() const
    554 {
    555     return gain;
    556 }
    557 
    558 /*
    559  * wrappers old <-> new parameters
    560  */
    561 float FilterParams::baseqFromOldPq(int Pq)
    562 {
    563     return expf(powf((float) Pq / 127.0f, 2) * logf(1000.0f)) - 0.9f;
    564 }
    565 
    566 float FilterParams::gainFromOldPgain(int Pgain)
    567 {
    568     return (Pgain / 64.0f - 1.0f) * 30.0f; //-30..30dB
    569 }
    570 
    571 float FilterParams::basefreqFromOldPreq(int Pfreq)
    572 {
    573     float tmp = (Pfreq / 64.0f - 1.0f) * 5.0f;
    574     return powf(2.0f, tmp + 9.96578428f);
    575 }
    576 
    577 /*
    578  * Get the center frequency of the formant's graph
    579  */
    580 float FilterParams::getcenterfreq() const
    581 {
    582     return 10000.0f * powf(10, -(1.0f - Pcenterfreq / 127.0f) * 2.0f);
    583 }
    584 
    585 /*
    586  * Get the number of octave that the formant functions applies to
    587  */
    588 float FilterParams::getoctavesfreq() const
    589 {
    590     return 0.25f + 10.0f * Poctavesfreq / 127.0f;
    591 }
    592 
    593 /*
    594  * Get the frequency from x, where x is [0..1]
    595  */
    596 float FilterParams::getfreqx(float x) const
    597 {
    598     if(x > 1.0f)
    599         x = 1.0f;
    600     float octf = powf(2.0f, getoctavesfreq());
    601     return getcenterfreq() / sqrt(octf) * powf(octf, x);
    602 }
    603 
    604 /*
    605  * Get the x coordinate from frequency (used by the UI)
    606  */
    607 float FilterParams::getfreqpos(float freq) const
    608 {
    609     return (logf(freq) - logf(getfreqx(0.0f))) / logf(2.0f) / getoctavesfreq();
    610 }
    611 
    612 /*
    613  * Transforms a parameter to the real value
    614  */
    615 float FilterParams::getformantfreq(unsigned char freq) const
    616 {
    617     return getfreqx(freq / 127.0f);
    618 }
    619 
    620 float FilterParams::getformantamp(unsigned char amp) const
    621 {
    622     return powf(0.1f, (1.0f - amp / 127.0f) * 4.0f);
    623 }
    624 
    625 float FilterParams::getformantq(unsigned char q) const
    626 {
    627     //temp
    628     return  powf(25.0f, (q - 32.0f) / 64.0f);
    629 }
    630 
    631 
    632 
    633 void FilterParams::add2XMLsection(XMLwrapper& xml, int n)
    634 {
    635     int nvowel = n;
    636     for(int nformant = 0; nformant < FF_MAX_FORMANTS; ++nformant) {
    637         xml.beginbranch("FORMANT", nformant);
    638         xml.addpar("freq", Pvowels[nvowel].formants[nformant].freq);
    639         xml.addpar("amp", Pvowels[nvowel].formants[nformant].amp);
    640         xml.addpar("q", Pvowels[nvowel].formants[nformant].q);
    641         xml.endbranch();
    642     }
    643 }
    644 
    645 void FilterParams::add2XML(XMLwrapper& xml)
    646 {
    647     //filter parameters
    648     xml.addpar("category", Pcategory);
    649     xml.addpar("type", Ptype);
    650     xml.addparreal("basefreq", basefreq);
    651     xml.addparreal("baseq", baseq);
    652     xml.addpar("stages", Pstages);
    653     xml.addparreal("freq_tracking", freqtracking);
    654     xml.addparreal("gain",       gain);
    655 
    656     //formant filter parameters
    657     if((Pcategory == 1) || (!xml.minimal)) {
    658         xml.beginbranch("FORMANT_FILTER");
    659         xml.addpar("num_formants", Pnumformants);
    660         xml.addpar("formant_slowness", Pformantslowness);
    661         xml.addpar("vowel_clearness", Pvowelclearness);
    662         xml.addpar("center_freq", Pcenterfreq);
    663         xml.addpar("octaves_freq", Poctavesfreq);
    664         for(int nvowel = 0; nvowel < FF_MAX_VOWELS; ++nvowel) {
    665             xml.beginbranch("VOWEL", nvowel);
    666             add2XMLsection(xml, nvowel);
    667             xml.endbranch();
    668         }
    669         xml.addpar("sequence_size", Psequencesize);
    670         xml.addpar("sequence_stretch", Psequencestretch);
    671         xml.addparbool("sequence_reversed", Psequencereversed);
    672         for(int nseq = 0; nseq < FF_MAX_SEQUENCE; ++nseq) {
    673             xml.beginbranch("SEQUENCE_POS", nseq);
    674             xml.addpar("vowel_id", Psequence[nseq].nvowel);
    675             xml.endbranch();
    676         }
    677         xml.endbranch();
    678     }
    679 }
    680 
    681 
    682 void FilterParams::getfromXMLsection(XMLwrapper& xml, int n)
    683 {
    684     int nvowel = n;
    685     for(int nformant = 0; nformant < FF_MAX_FORMANTS; ++nformant) {
    686         if(xml.enterbranch("FORMANT", nformant) == 0)
    687             continue;
    688         Pvowels[nvowel].formants[nformant].freq = xml.getpar127(
    689             "freq",
    690             Pvowels[nvowel
    691             ].formants[nformant].freq);
    692         Pvowels[nvowel].formants[nformant].amp = xml.getpar127(
    693             "amp",
    694             Pvowels[nvowel
    695             ].formants[nformant].amp);
    696         Pvowels[nvowel].formants[nformant].q =
    697             xml.getpar127("q", Pvowels[nvowel].formants[nformant].q);
    698         xml.exitbranch();
    699     }
    700 }
    701 
    702 void FilterParams::getfromXML(XMLwrapper& xml)
    703 {
    704     const bool upgrade_3_0_2 = (xml.fileversion() < version_type(3,0,2)) && (xml.getparreal("basefreq", -1) < 0);
    705 
    706     //filter parameters
    707     Pcategory    = xml.getpar127("category", Pcategory);
    708     Ptype        = xml.getpar127("type", Ptype);
    709     Pstages      = xml.getpar127("stages", Pstages);
    710     if(upgrade_3_0_2) {
    711         int Pfreq = xml.getpar127("freq", 0);
    712         basefreq  = (Pfreq / 64.0f - 1.0f) * 5.0f;
    713         basefreq  = powf(2.0f, basefreq + 9.96578428f);
    714         int Pq    = xml.getpar127("q", 0);
    715         baseq     = expf(powf((float) Pq / 127.0f, 2) * logf(1000.0f)) - 0.9f;
    716         int Pgain = xml.getpar127("gain", 0);
    717         gain      = (Pgain / 64.0f - 1.0f) * 30.0f; //-30..30dB
    718         int Pfreqtracking = xml.getpar127("freq_track", 0);
    719         freqtracking = 100 * (Pfreqtracking - 64.0f) / (64.0f);
    720     } else {
    721         basefreq     = xml.getparreal("basefreq",   1000);
    722         baseq        = xml.getparreal("baseq",      10);
    723         gain         = xml.getparreal("gain",       0);
    724         freqtracking = xml.getparreal("freq_tracking", 0);
    725     }
    726     float basefreq_min = std::atof(ports["basefreq"]->meta()["min"]);
    727     basefreq = std::max(basefreq, basefreq_min);
    728 
    729     //formant filter parameters
    730     if(xml.enterbranch("FORMANT_FILTER")) {
    731         Pnumformants     = xml.getpar127("num_formants", Pnumformants);
    732         Pformantslowness = xml.getpar127("formant_slowness", Pformantslowness);
    733         Pvowelclearness  = xml.getpar127("vowel_clearness", Pvowelclearness);
    734         Pcenterfreq      = xml.getpar127("center_freq", Pcenterfreq);
    735         Poctavesfreq     = xml.getpar127("octaves_freq", Poctavesfreq);
    736 
    737         for(int nvowel = 0; nvowel < FF_MAX_VOWELS; ++nvowel) {
    738             if(xml.enterbranch("VOWEL", nvowel) == 0)
    739                 continue;
    740             getfromXMLsection(xml, nvowel);
    741             xml.exitbranch();
    742         }
    743         Psequencesize     = xml.getpar127("sequence_size", Psequencesize);
    744         Psequencestretch  = xml.getpar127("sequence_stretch", Psequencestretch);
    745         Psequencereversed = xml.getparbool("sequence_reversed",
    746                                             Psequencereversed);
    747         for(int nseq = 0; nseq < FF_MAX_SEQUENCE; ++nseq) {
    748             if(xml.enterbranch("SEQUENCE_POS", nseq) == 0)
    749                 continue;
    750             Psequence[nseq].nvowel = xml.getpar("vowel_id",
    751                                                  Psequence[nseq].nvowel,
    752                                                  0,
    753                                                  FF_MAX_VOWELS - 1);
    754             xml.exitbranch();
    755         }
    756         xml.exitbranch();
    757     }
    758 }
    759 
    760 #define COPY(y) this->y = x.y
    761 void FilterParams::paste(FilterParams &x)
    762 {
    763     COPY(Pcategory);
    764     COPY(Ptype);
    765     COPY(basefreq);
    766     COPY(Pstages);
    767     COPY(freqtracking);
    768     COPY(gain);
    769 
    770     COPY(Pnumformants);
    771     COPY(Pformantslowness);
    772     COPY(Pvowelclearness);
    773     COPY(Pcenterfreq);
    774     COPY(Poctavesfreq);
    775 
    776     for(int i=0; i<FF_MAX_VOWELS; ++i) {
    777         for(int j=0; j<FF_MAX_FORMANTS; ++j) {
    778             auto &a = this->Pvowels[i].formants[j];
    779             auto &b = x.Pvowels[i].formants[j];
    780             a.freq = b.freq;
    781             a.amp  = b.amp;
    782             a.q    = b.q;
    783         }
    784     }
    785 
    786 
    787     COPY(Psequencesize);
    788     COPY(Psequencestretch);
    789     COPY(Psequencereversed);
    790     for(int i=0; i<FF_MAX_SEQUENCE; ++i)
    791         this->Psequence[i] = x.Psequence[i];
    792 
    793     COPY(changed);
    794 
    795     if ( time ) {
    796         last_update_timestamp = time->time();
    797     }
    798 }
    799 #undef COPY
    800 
    801 void FilterParams::pasteArray(FilterParams &x, int nvowel)
    802 {
    803     for(int nformant = 0; nformant < FF_MAX_FORMANTS; ++nformant) {
    804         auto &self   = Pvowels[nvowel].formants[nformant];
    805         auto &update = x.Pvowels[nvowel].formants[nformant];
    806         self.freq = update.freq;
    807         self.amp  = update.amp;
    808         self.q    = update.q;
    809     }
    810 
    811     if ( time ) {
    812         last_update_timestamp = time->time();
    813     }
    814 }
    815 
    816 }