# data file for the Fltk User Interface Designer (fluid)
version 1.0304
header_name {.h}
code_name {.cc}
comment {FilterUI.h} {not_in_source in_header
}

comment {FilterUI.cc} {in_source not_in_header
}

comment {Original ZynAddSubFX author Nasca Octavian Paul
Copyright (C) 2002-2005 Nasca Octavian Paul
Copyright 2009-2011, Alan Calvert
Copyright 2016-2021 Will Godfrey

This file is part of yoshimi, which is free software: you can redistribute
it and/or modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.

yoshimi is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.   See the GNU General Public License (version 2 or
later) for more details.

You should have received a copy of the GNU General Public License along with
yoshimi; if not, write to the Free Software Foundation, Inc., 51 Franklin
Street, Fifth Floor, Boston, MA  02110-1301, USA.

This file is derivative of ZynAddSubFX original code

} {selected in_source in_header
}

decl {\#include <iostream>} {public local
}

decl {\#include "UI/MiscGui.h"} {public local
}

decl {\#include "UI/WidgetCheckButton.h"} {public local
}

decl {\#include "UI/WidgetPDial.h"} {public local
}

decl {\#include "UI/WidgetMWSlider.h"} {public local
}

decl {\#include <FL/Fl_Group.H>} {public local
}

decl {\#include <FL/Fl_Box.H>} {public local
}

decl {\#include <FL/fl_draw.H>} {public local
}

decl {\#include <FL/fl_ask.H>} {public local
}

decl {\#include "PresetsUI.h"} {public local
}

decl {\#include "Params/FilterParams.h"} {public local
}

decl {\#include <FL/Fl.H>} {private global
}

decl {\#include <FL/names.h>} {private global
}

decl {\#include "MasterUI.h"} {private global
}

decl {\#include "Misc/SynthEngine.h"} {public global
}

decl {class FilterUI;} {public global
}

decl {\#include "Misc/FormatFuncs.h"
    using func::asString;} {private local
}

decl {\#include "Misc/NumericFuncs.h"
    using func::rap2dB;
    using func::limit;} {private local
}

class FormantFilterGraph {: {public Fl_Box}
} {
  Function {FormantFilterGraph(int x, int y, int w, int h, FilterUI& parent_,
            FilterParams *pars_, int *nvowel_, int *nformant_):
      Fl_Box(x,y,w,h), parent(parent_), pars(pars_), nvowel(nvowel_), nformant(nformant_)} {} {
    code {//
        selectedFormant = -1;
        hoverFormant = -1;
        qMode = false;
        graphpoints = new float [w];} {}
  }
  Function {draw_freq_line(float freq)} {} {
    code {//
        float freqx = pars->getfreqpos(freq);
        if (freqx > 0.0 && freqx < 1.0)
            fl_line(x() + (int) (freqx * w()), y(), x() + (int)(freqx * w()), y() + h());} {}
  }
  Function {draw()} {} {
    code {//
        int maxdB = 30;
        int ox = x(), oy = y(), lx = w(), ly = h(), i, oiy;
        float freqx;

        int points = 475;
        float scale = lx / float(points);
        fl_line_style(0, 1 * scale); // part of new resizable code

        fl_color(FL_BLACK);
        fl_rectf(ox, oy, lx, ly);

        //draw the lines
        fl_color(FL_GRAY);

        freqx = pars->getfreqpos(1000.0);
        if (freqx > 0.0 && freqx < 1.0)
            fl_line(ox + (int)(freqx * lx), oy, ox + (int)(freqx * lx), oy + ly);

        for (i = 1; i < 10; ++i)
        {
            if (i == 1)
            {
                draw_freq_line(i * 100.0);
                draw_freq_line(i * 1000.0);
            }
            else if (i == 5)
            {
                draw_freq_line(i * 100.0);
                draw_freq_line(i * 1000.0);
            }
            else
            {
                draw_freq_line(i * 100.0);
                draw_freq_line(i * 1000.0);
            }
        }

        draw_freq_line(10000.0);
        draw_freq_line(20000.0);

        int GY = 10;
        if (ly < GY * 3 )
            GY = -1;
        for (i = 1; i < GY; ++i)
        {
            int tmp = (int)(ly / (float)GY * i);
            fl_line(ox + 2, oy + tmp, ox + lx - 2, oy + tmp);
        }

        fl_line_style(0, 2 * scale);
        fl_color(FL_YELLOW);
        fl_font(FL_HELVETICA, 10 * scale);
        if (*nformant < pars->Pnumformants)
        {
            draw_freq_line(pars->getformantfreq(pars->Pvowels[*nvowel].formants[*nformant].freq));

            //show some information (like current formant frequency,amplitude)
            string tmpstr = asString((float)
                (pars->getformantfreq(pars->Pvowels[*nvowel].formants[*nformant].freq) / 1000.0))
                    + " kHz";
            fl_draw(tmpstr.c_str(), ox + 2 * scale, oy + 2 * scale, 40 * scale, 12 * scale, FL_ALIGN_LEFT, NULL, 0);
            tmpstr = asString((int)
                (rap2dB(1e-9 + pars->getformantamp(pars->Pvowels[*nvowel].formants[*nformant].amp)) + pars->getgain()))
                    + " dB";
            fl_draw(tmpstr.c_str(), ox + 2 * scale, oy + 15 * scale, 40 * scale, 12 * scale, FL_ALIGN_LEFT, NULL, 0);
        }

        if (hoverFormant >= 0 && hoverFormant != *nformant)
        {
            fl_color(fl_darker(FL_YELLOW));
            draw_freq_line(pars->getformantfreq(pars->Pvowels[*nvowel].formants[hoverFormant].freq));
        }

        // draw the data
        fl_color(FL_RED);

        pars->formantfilterH(*nvowel, points, graphpoints);

        int lastpos = ox;
        oiy = (int) ((graphpoints[0] / maxdB + 1.0) * ly / 2.0);
        for (i = 1; i < points; ++i)
        {
            int nextpos = ox + int(i * scale);
            int iy = (int) ((graphpoints[i] / maxdB + 1.0) * ly / 2.0);
            if (iy >= 0 && oiy >= 0 && iy < ly && oiy < lx)
                fl_line(lastpos, oy + ly - oiy, nextpos, oy + ly - iy);
            oiy = iy;
            lastpos = nextpos;
        }
        parent.formantRtext();
        fl_line_style(0,1);

        /*  original version

        pars->formantfilterH(*nvowel, lx, graphpoints);

        oiy = (int) ((graphpoints[0] / maxdB + 1.0) * ly / 2.0);
        for (i = 1; i < lx; ++i)
        {
            int iy = (int) ((graphpoints[i] / maxdB + 1.0) * ly / 2.0);
            if (iy >= 0 && oiy >= 0 && iy < ly && oiy < lx)
                fl_line(ox + i - 1, oy + ly - oiy, ox, oy + ly - iy);
            oiy = iy;
        }
        */} {}
  }
  Function {~FormantFilterGraph()} {} {
    code {delete [] graphpoints;} {}
  }
  decl {FilterUI& parent;} {private local
  }
  decl {FilterParams *pars;} {private local
  }
  decl {int const *nvowel, *nformant;} {private local
  }
  decl {float *graphpoints;} {private local
  }
  decl {int selectedFormant, hoverFormant, yRef, xRef, qRef, ampRef, freqRef, cFreqRef;} {private local
  }
  decl {bool qMode;} {private local
  }
  Function {handle(int event)} {return_type int
  } {
    code {//
switch(event)
{
  case FL_ENTER: // enable keyboard and drag events
      Fl::focus(this);
      Fl::belowmouse(this);
      fl_cursor(FL_CURSOR_HAND);
      return 1;
  case FL_MOVE: // find formant closest to cursor
  {
      int minDiff = INT_MAX;
      int relPos = (int)(127.0f * ((float) Fl::event_x() / w()));
      for (int i = 0; i < pars->Pnumformants; ++i)
      {
          int diff = abs(pars->Pvowels[*nvowel].formants[i].freq - relPos);
          if (diff < minDiff)
          {
              minDiff = diff;
              hoverFormant = i;
          }
      }
      redraw();
      return 1;
  }
  case FL_PUSH:
      if (Fl::event_key() > FL_Button + FL_RIGHT_MOUSE)
      {
          handle(FL_KEYDOWN); // Non-LMB/MMB/RMB button - check for forward/backward
          return 0;
      }

      Fl::pushed(this);

       // select formant closest to cursor
      if (selectedFormant < 0 && Fl::event_button() != FL_MIDDLE_MOUSE)
      {
          selectedFormant = hoverFormant;
          delegate(parent.formantnumber, hoverFormant);
          hoverFormant = -1;
      }

      //Activate w. RMB, retain state when using MMB
      qMode = Fl::event_button() == FL_RIGHT_MOUSE
          || (Fl::event_button() == FL_MIDDLE_MOUSE && qMode);

      update_refs();

      return 1;
  case FL_DRAG: // change frequency/amplitude/q for selected formant
  {

      int hDiff = 127 * (xRef - Fl::event_x()) / w();
      int vDiff = 127 * (yRef - Fl::event_y()) / h();

      if (Fl::event_button2()) // Always prioritize center frequency changes
      {
          fl_cursor(FL_CURSOR_WE);
          delegate(parent.cfknob, limit(cFreqRef + hDiff, 0, 127));
      }
      else
      {
         fl_cursor(FL_CURSOR_MOVE);
          delegate(parent.formant_freq_dial, limit(freqRef - hDiff, 0, 127));

          if (qMode)
              delegate(parent.formant_q_dial, limit(qRef + vDiff, 0, 127));
          else
              delegate(parent.formant_amp_dial, limit(ampRef + vDiff, 0, 127));
      }
      return 1;
  }
  case FL_KEYDOWN: // changes active vowel with left/right , x/z, or forward/back mouse buttons

      if (Fl::event_key(FL_Left) || Fl::event_key(122) || Fl::event_key() == FL_Button + 8)
      {
          delegate(parent.vowelnumber, (FF_MAX_VOWELS + ((*nvowel) - 1)) % FF_MAX_VOWELS);
          hoverFormant = -1;
      }
      else if (Fl::event_key(FL_Right) || Fl::event_key(120) || Fl::event_key() == FL_Button + 9)
      {
          delegate(parent.vowelnumber, ((*nvowel) + 1) % FF_MAX_VOWELS);
          hoverFormant = -1;
      }
      return 1;
  case FL_MOUSEWHEEL:
  {
      int offset = Fl::event_dy() * (Fl::event_ctrl() ? 1 : 4);
      if (Fl::event_shift())
          delegate(parent.cfknob, limit(pars->Pcenterfreq + offset, 0, 127));
      else
          delegate(parent.octknob, limit(pars->Poctavesfreq + offset, 0, 127));
      return 1;
  }
  case FL_RELEASE:

      if (Fl::event_inside(this))
          fl_cursor(FL_CURSOR_HAND);

      if (Fl::event_button() == FL_RIGHT_MOUSE)
          qMode = false;
      else if (Fl::event_button() == FL_LEFT_MOUSE)
          qMode = true;

      if (!Fl::event_button1() && !Fl::event_button3())
      {
          selectedFormant = -1;
          return 1;
      }
      else
      {
          update_refs();
      }
      Fl::pushed(this);
      return 1;
  case FL_LEAVE:
      if (!Fl::event_inside(this)) // Cover for some strange events when clicking
      {
          hoverFormant = -1;
          qMode = false;
          redraw();
          fl_cursor(FL_CURSOR_DEFAULT);
      }
      return 1;
}

return Fl_Box::handle(event);} {}
  }
  Function {delegate(Fl_Valuator* w, int value)} {private return_type void
  } {
    code {// If widget value has changed, update and perform manual callback

        if (w->value() != value)
        {
            w->value(value);
            w->do_callback();
        }} {}
  }
  Function {update_refs()} {private return_type {inline void}
  } {
    code {//
        yRef = Fl::event_y();
        xRef = Fl::event_x();
        ampRef = pars->Pvowels[*nvowel].formants[selectedFormant].amp;
        freqRef = pars->Pvowels[*nvowel].formants[selectedFormant].freq;
        qRef = pars->Pvowels[*nvowel].formants[selectedFormant].q;
        cFreqRef = pars->Pcenterfreq;} {}
  }
}

class FilterUI {: {public Fl_Group,PresetsUI_}
} {
  Function {FilterUI(int x,int y, int w, int h, const char *label=0):Fl_Group(x,y,w,h,label)} {} {
    code {//
        synth = NULL;
        pars=NULL;
        velsnsamp=NULL;
        velsns=NULL;
        nvowel = 0;
        nformant = 0;
        nseqpos = 0;} {}
  }
  Function {~FilterUI()} {} {
    code {filterui->hide();
        formantparswindow->hide();
        hide();
        delete (formantparswindow);} {}
  }
  Function {make_window()} {} {
    Fl_Window filterui {
      xywh {460 79 275 75} type Double color 247 labelfont 1 labelsize 12 hide
      class Fl_Group
    } {
      Fl_Group filterparamswindow {
        label {             Filter Params}
        xywh {0 0 275 75} box PLASTIC_UP_BOX color 231 labelfont 1 labelsize 10 align 17
      } {
        Fl_Choice analogfiltertypechoice {
          label FilterType
          callback {//
          send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::analogType, o->value(), TOPLEVEL::type::Write);}
          tooltip {The Filter type} xywh {5 43 58 15} down_box BORDER_BOX labelsize 10 align 6 textfont 1 textsize 10
          code1 {o->value(pars->Ptype);}
          code2 {o->add("Low1");o->add("High1");o->add("Low2");o->add("High2");o->add("Band2");o->add("Stop2");o->add("Peak2");o->add("LShelf2");o->add("HShelf2");}
        } {}
        Fl_Choice svfiltertypechoice {
          label FilterType
          callback {//
          send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::stateVariableType, o->value(), TOPLEVEL::type::Write);}
          tooltip {The Filter type} xywh {5 43 58 15} down_box BORDER_BOX labelsize 10 align 6 textfont 1 textsize 10
          code1 {o->value(pars->Ptype);}
          code2 {o->add("Low");o->add("High");o->add("Band");o->add("Stop");}
        } {}
        Fl_Choice filtertype {
          label Category
          callback {//
          send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::baseType, o->value(), TOPLEVEL::type::Write);}
          tooltip {The Category of the Filter (Analog/Formantic/etc.)} xywh {5 10 66 15} down_box BORDER_BOX labelsize 10 align 6 textfont 1 textsize 10
          code0 {o->value(pars->Pcategory);}
          code1 {o->add("analog");o->add("formant");o->add("StVarF");}
        } {}
        Fl_Dial cfreqdial {
          label {C.Freq}
          callback {//
          send_data(0, FILTERINSERT::control::centerFrequency, o->value(), TOPLEVEL::type::Write);}
          tooltip {Center Frequency of the Filter or the base position in the vowel's sequence} xywh {65 28 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1
          code0 {o->value(pars->Pfreq);}
          code1 {o->setValueType(getFilterFreqType(filtertype->value()));}
          class WidgetPDial
        }
        Fl_Dial qdial {
          label Q
          callback {//
          send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::Q, o->value(), TOPLEVEL::type::Write);}
          tooltip {Filter resonance or bandwidth} xywh {100 28 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1
          code0 {o->value(pars->Pq);}
          code1 {updateVCforQ();}
          class WidgetPDial
        }
        Fl_Dial freqtrdial {
          label {Freq.tr.}
          callback {//
          send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::frequencyTracking, o->value(), TOPLEVEL::type::Write);}
          tooltip {Filter frequency tracking} xywh {240 28 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1
          code1 {o->value(pars->Pfreqtrack);}
          class WidgetPDial
        }
        Fl_Dial vsnsadial {
          label {V.SnsA.}
          callback {//
          send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::velocitySensitivity, o->value(), TOPLEVEL::type::Write);}
          tooltip {Velocity sensing amount of the Filter} xywh {135 28 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1
          code0 {o->setValueType(VC_FilterVelocityAmp);}
          class WidgetPDial
        }
        Fl_Dial vsnsdial {
          label {V.Sns.}
          callback {//
          send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::velocityCurve, o->value(), TOPLEVEL::type::Write);}
          tooltip {Velocity Sensing Function of the Filter} xywh {170 28 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1
          code0 {o->setValueType(VC_FilterVelocitySense);}
          code1 {o->setGraphicsType(VC_FilterVelocitySense);}
          class WidgetPDial
        }
        Fl_Dial gaindial {
          label Gain
          callback {//
          send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::gain, o->value(), TOPLEVEL::type::Write);}
          tooltip {Filter output gain/damp} xywh {205 28 30 30} box ROUND_UP_BOX labelsize 10 maximum 127 step 1
          code0 {o->value(pars->Pgain);}
          code1 {o->setValueType(VC_FilterGain);}
          class WidgetPDial
        }
        Fl_Counter stcounter {
          label {St.}
          callback {//
          send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::stages, o->value() - 1, TOPLEVEL::type::Write);

// stages has been fixed at 5 (current max stages) until a programatic
// way can be found to set max to this value}
          tooltip {Filter stages (in order to increase dB/oct. value and the order of the filter)} xywh {78 10 31 15} type Simple labelsize 10 align 8 minimum 1 maximum 5 step 1 value 1 textfont 1 textsize 10
        }
      }
      Fl_Button editbutton {
        label Edit
        callback {//
    int X, Y, W, H, O;
    int type = engine;
    if (type > 2)
        type = 3;
    //std::cout << "type open " << type << std::endl;
    loadWin(synth, W, H, X, Y, O, "xFilter-formant " + to_string(type));
    if (W < formantDW || H < formantDH)
    {
        W = formantDW;
        H = formantDH;
    }
    checkSane(X, Y, W, H , formantDW, formantDH);
    formantparswindow->resize(X, Y, W, H);
    lastformantX = 0;
    lastformantY = 0;
    lastformantW = 0;
    formantSeen = true;
    formantRtext();
    formantparswindow->show();}
        xywh {5 40 52 26} box PLASTIC_UP_BOX labelfont 1 labelsize 11
      }
      Fl_Button filtC {
        label C
        callback {synth->getGuiMaster()->getPresetsUi()->copy(pars);}
        xywh {202 8 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7
      }
      Fl_Button filtP {
        label P
        callback {synth->getGuiMaster()->getPresetsUi()->paste(pars,this);}
        xywh {220 8 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7
      }
      Fl_Check_Button freqtrackoffset {
        label { 0 /+}
        callback {//
        send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::frequencyTrackingRange, o->value(), TOPLEVEL::type::Write);}
        tooltip {Set frequency tracking range to 0%-200%} xywh {238 10 33 16} down_box DOWN_BOX labelsize 10 align 24
        class Fl_Check_Button2
      }
    }
  }
  Function {make_formant_window()} {} {
    Fl_Window formantparswindow {
      label {Formant Filter Parameters}
      callback {formClose->do_callback();}
      xywh {94 78 700 205} type Double labelfont 1 labelsize 12 hide resizable
      code0 {formantDW = o->w(); formantDH = o->h();}
      code1 {o->size_range(formantDW, formantDH, 0, 0, 0, 0, 1);}
    } {
      Fl_Group {} {
        xywh {1 1 698 203} box ENGRAVED_FRAME
      } {
        Fl_Group {} {
          xywh {485 47 105 113} box THIN_UP_BOX
        } {
          Fl_Counter formantnumber {
            label {Formant }
            callback {//
          nformant = (int) o->value();
          update_formant_window();
          formantfiltergraph->redraw();
          //send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::formantNumber, o->value(), TOPLEVEL::type::Write);}
            xywh {545 80 40 15} type Simple labelfont 1 labelsize 10 align 4 minimum 0 maximum 127 step 1 textsize 10
            code0 {o->bounds(0,FF_MAX_FORMANTS-1);}
            code1 {o->value(nformant);}
          }
          Fl_Counter vowelnumber {
            label {Vowel no.}
            callback {//
          nvowel = (int) o->value();
          update_formant_window();
          formantfiltergraph->redraw();
          //send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::vowelNumber, o->value(), TOPLEVEL::type::Write);}
            xywh {545 55 40 20} type Simple labelfont 1 labelsize 10 align 4 minimum 0 maximum 127 step 1 textfont 1 textsize 11
            code0 {o->bounds(0,FF_MAX_VOWELS-1);}
            code1 {o->value(nvowel);}
          }
          Fl_Group formantparsgroup {
            xywh {490 105 95 50} box ENGRAVED_FRAME
          } {
            Fl_Dial formant_freq_dial {
              label Freq
              callback {//
            send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::formantFrequency, o->value(), TOPLEVEL::type::Write, nformant, nvowel);}
              tooltip {Formant frequency} xywh {495 115 25 25} box ROUND_UP_BOX labelsize 10 maximum 127 step 1
              class WidgetPDial
            }
            Fl_Dial formant_q_dial {
              label Q
              callback {//
            send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::formantQ, o->value(), TOPLEVEL::type::Write, nformant, nvowel);}
              tooltip {Formant's Q} xywh {525 115 24 25} box ROUND_UP_BOX labelsize 10 maximum 127 step 1
              class WidgetPDial
            }
            Fl_Dial formant_amp_dial {
              label Amp
              callback {//
            send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::formantAmplitude, o->value(), TOPLEVEL::type::Write, nformant, nvowel);}
              tooltip {Formant amplitude} xywh {555 115 24 25} box ROUND_UP_BOX labelsize 10 maximum 127 step 1
              class WidgetPDial
            }
          }
        }
        Fl_Group {} {
          xywh {590 47 102 113} box THIN_UP_BOX
        } {
          Fl_Counter seqsize {
            label {Seq.Size}
            callback {//
          send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::sequenceSize, o->value(), TOPLEVEL::type::Write);}
            xywh {595 62 55 20} type Simple labelfont 1 labelsize 10 align 5 minimum 0 maximum 127 step 1 textfont 1 textsize 11
            code0 {o->bounds(1,FF_MAX_SEQUENCE);}
            code1 {o->value(pars->Psequencesize);}
          }
          Fl_Counter seqpos {
            label {S.Pos.}
            callback {//
          nseqpos = (int) o->value();
          update_formant_window();
          send_data(0, FILTERINSERT::control::sequencePosition, o->value(), TOPLEVEL::type::Write);}
            tooltip {Current position in the sequence} xywh {595 97 40 15} type Simple labelfont 1 labelsize 10 align 9 minimum 0 maximum 127 step 1 textsize 10
            code0 {o->bounds(0,FF_MAX_SEQUENCE-1);}
            code1 {o->value(nseqpos);}
          }
          Fl_Counter vowel_counter {
            label Vowel
            callback {//
          send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::vowelPositionInSequence, o->value(), TOPLEVEL::type::Write, nseqpos);}
            tooltip {Vowel number at the sequence position} xywh {640 97 40 15} type Simple labelsize 10 align 1 minimum 0 maximum 127 step 1 textsize 10
            code0 {o->bounds(0,FF_MAX_VOWELS-1);}
          }
          Fl_Check_Button neginput {
            label {Neg.Input}
            callback {//
          send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::negateInput, o->value(), TOPLEVEL::type::Write);}
            tooltip {Negate the input from LFO/envelopes/etc.} xywh {625 132 60 20} down_box DOWN_BOX labelsize 10
            code0 {o->value(pars->Psequencereversed);}
            class Fl_Check_Button2
          }
          Fl_Dial strchdial {
            label Strch
            callback {//
          send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::formantStretch, o->value(), TOPLEVEL::type::Write);}
            tooltip {Sequence Stretch} xywh {595 130 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1
            code0 {o->value(pars->Psequencestretch);}
            code1 {o->setValueType(VC_FormFilterStretch);}
            class WidgetPDial
          }
        }
        Fl_Counter formantcount {
          label Formants
          callback {//
        send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::numberOfFormants, o->value(), TOPLEVEL::type::Write);}
          xywh {485 15 65 20} type Simple labelfont 1 labelsize 10 align 1 minimum 0 maximum 127 step 1 textsize 12
          code0 {o->bounds(1,FF_MAX_FORMANTS);}
          code1 {o->value(pars->Pnumformants);}
        }
        Fl_Dial frsldial {
          label {Fr.Sl.}
          callback {//
        send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::formantSlowness, o->value(), TOPLEVEL::type::Write);}
          tooltip {Formant's Slowness (Morphing)} xywh {565 15 25 25} box ROUND_UP_BOX labelfont 1 labelsize 10 align 1 maximum 127 step 1
          code0 {o->value(pars->Pformantslowness);}
          code1 {o->setValueType(VC_FormFilterSlowness);}
          class WidgetPDial
        }
        Fl_Value_Output centerfreqvo {
          label {C.f.}
          callback {o->value(pars->getcenterfreq()/1000.0);}
          tooltip {Center Frequency (kHz)} xywh {512 164 35 16} box THIN_DOWN_BOX labelsize 12 when 3 minimum 1 maximum 10 value 1 textfont 1 textsize 11
          code0 {o->step(0.01, 1);}
          code1 {o->value(pars->getcenterfreq()/1000.0);}
        }
        Fl_Value_Output octavesfreqvo {
          label {Oct.}
          callback {o->value(pars->getoctavesfreq());}
          tooltip {No. of octaves} xywh {512 185 35 16} box THIN_DOWN_BOX labelsize 12 when 3 minimum 1 maximum 127 value 5 textfont 1 textsize 11
          code0 {o->step(0.01, 1);}
          code1 {o->value(pars->getoctavesfreq());}
        }
        Fl_Slider cfknob {
          callback {//
        if (Fl::event_button() == 3)
            o->value(64);
        send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::formantCenter, o->value(), TOPLEVEL::type::Write);}
          xywh {547 164 89 16} type {Horz Knob} box THIN_DOWN_BOX maximum 127 step 1
          code0 {o->value(pars->Pcenterfreq);}
          class mwheel_slider_rev
        }
        Fl_Slider octknob {
          callback {//
        if (Fl::event_button() == 3)
            o->value(64);
        send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::formantOctave, o->value(), TOPLEVEL::type::Write);}
          xywh {547 185 90 16} type {Horz Knob} box THIN_DOWN_BOX maximum 127 step 1
          code0 {o->value(pars->Poctavesfreq);}
          class mwheel_slider_rev
        }
        Fl_Dial wvknob {
          label {Vw.Cl.}
          callback {//
        send_data(TOPLEVEL::action::forceUpdate, FILTERINSERT::control::formantClearness, o->value(), TOPLEVEL::type::Write);}
          tooltip {Vowel "clearness" (transition between vowels)} xywh {600 15 25 25} box ROUND_UP_BOX labelfont 1 labelsize 10 align 1 maximum 127 step 1
          code0 {o->value(pars->Pvowelclearness);}
          code1 {o->setValueType(VC_FormFilterClearness);}
          code2 {o->setGraphicsType(VC_FormFilterClearness);}
          class WidgetPDial
        }
        Fl_Button formClose {
          label Close
          callback {//
    int type = engine;
    if (engine > 2)
        type = 3;
    //std::cout << "type close " << type << std::endl;
    if (formantSeen)
        saveWin(synth, formantparswindow->w(), formantparswindow->h(), formantparswindow->x(), formantparswindow->y(), false, "xFilter-formant " +to_string(type));
    formantSeen = false,
    formantparswindow->hide();
    lastformantW = 0;}
          xywh {643 170 50 25} box THIN_UP_BOX
        }
        Fl_Button formCopy {
          label C
          callback {synth->getGuiMaster()->getPresetsUi()->copy(pars,nvowel);}
          xywh {635 25 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7
        }
        Fl_Button formPaste {
          label P
          callback {synth->getGuiMaster()->getPresetsUi()->paste(pars,this,nvowel);}
          xywh {665 25 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7
        }
        Fl_Box formvowel {
          label Vowel
          xywh {635 7 55 15}
        }
      }
    }
    code {// Formant graph
{
      FormantFilterGraph* o = formantfiltergraph = new FormantFilterGraph(5, 5, 475, 195, *this, pars, &nvowel, &nformant);
      formantfiltergraph->box(FL_BORDER_BOX);
      formantparswindow->add(o);
} // FormantFilterGraph* formantfiltergraph} {}
  }
  Function {update_formant_window()} {} {
    code {//
        formant_freq_dial->value(pars->Pvowels[nvowel].formants[nformant].freq);
        formant_q_dial->value(pars->Pvowels[nvowel].formants[nformant].q);
        formant_amp_dial->value(pars->Pvowels[nvowel].formants[nformant].amp);
        if (nformant < pars->Pnumformants)
            formantparsgroup->activate();
        else
            formantparsgroup->deactivate();

        if (nseqpos < pars->Psequencesize)
            vowel_counter->activate();
        else
            vowel_counter->deactivate();

        vowel_counter->value(pars->Psequence[nseqpos].nvowel);} {}
  }
  Function {refresh()} {} {
    code {//
        update_formant_window();
        formantfiltergraph->redraw();
        int categ = pars->Pcategory;
        int parstype = pars->Ptype;
        filtertype->value(categ);
        cfreqdial->value(pars->Pfreq);
        freqtrackoffset->value(pars->Pfreqtrackoffset);
        freqtrdial->setValueType(getFilterFreqTrackType(freqtrackoffset->value()));
        qdial->value(pars->Pq);
        freqtrdial->value(pars->Pfreqtrack);
        gaindial->value(pars->Pgain);
        stcounter->value(fetchData(0, FILTERINSERT::control::stages, npart, kititem, engine, TOPLEVEL::insert::filterGroup) + 1);

        qdial->activate();
        if (categ != 1)
        {
            if (categ == 0)
            {
                analogfiltertypechoice->value(parstype);
                analogfiltertypechoice->show();
                svfiltertypechoice->hide();
                if (parstype < 2)
                    qdial->deactivate();
            }
            else
            {
                svfiltertypechoice->value(parstype);
                svfiltertypechoice->show();
                analogfiltertypechoice->hide();
            }
            editbutton->hide();
            formantparswindow->hide();
            cfreqdial->label("C.freq");
        }
        else
        {
            analogfiltertypechoice->hide();
            svfiltertypechoice->hide();
            editbutton->show();
            cfreqdial->label("BS.pos");
        }
        filterparamswindow->redraw();} {}
  }
  Function {filterRtext(float dScale)} {} {
    code {//
            int size = int(dScale * 10);

            filterparamswindow->labelsize(size);

            cfreqdial->labelsize(size);
            qdial->labelsize(size);
            freqtrdial->labelsize(size);
            vsnsadial->labelsize(size);
            vsnsdial->labelsize(size);
            gaindial->labelsize(size);

            filtertype->labelsize(size);
                filtertype->textsize(size);

            stcounter->labelsize(size);
            stcounter->textsize(size);

            analogfiltertypechoice->labelsize(size);
                analogfiltertypechoice->textsize(size);

            svfiltertypechoice->labelsize(size);
                svfiltertypechoice->textsize(size);

            editbutton->labelsize(size);
            filtC->labelsize(size);
            filtP->labelsize(size);

            freqtrackoffset->labelsize(size);} {}
  }
  Function {formantRtext()} {} {
    code {//
    /*
     * This is very different from other windows because it is called
     * via the draw method, not the usual chain.
    */

    if (lastformantX == formantparswindow->x() && lastformantY == formantparswindow->y() &&lastformantW == formantparswindow->w())
        return;
    lastformantX = formantparswindow->x();
    lastformantY = formantparswindow->y();
    lastformantW = formantparswindow->w();

    //std::cout << "formant seen" << std::endl;
    float dScale = formantparswindow->w() / float(formantDW);

    int size = int(10 * dScale);
    int size11 = int(11 * dScale);
    int size12 = int(12 * dScale);
    int size14 = int(14 * dScale);

    formantnumber->labelsize(size);
        formantnumber->textsize(size);
    vowelnumber->labelsize(size);
        vowelnumber->textsize(size11);

    formant_freq_dial->labelsize(size);
    formant_q_dial->labelsize(size);
    formant_amp_dial->labelsize(size);

    seqsize->labelsize(size);
        seqsize->textsize(size11);
    seqpos->labelsize(size);
        seqpos->textsize(size);
    vowel_counter->labelsize(size);
        vowel_counter->textsize(size);
    neginput->labelsize(size);
    strchdial->labelsize(size);
    formantcount->labelsize(size);
        formantcount->textsize(size12);
    frsldial->labelsize(size);
    centerfreqvo->labelsize(size12);
        centerfreqvo->textsize(size11);
    octavesfreqvo->labelsize(size12);
        octavesfreqvo->textsize(size11);
    wvknob->labelsize(size);

    formClose->labelsize(size14);
    formvowel->labelsize(size14);
    formCopy->labelsize(size11);
    formPaste->labelsize(size11);} {}
  }
  Function {send_data(int action, int control, float value, int type, int parameter = UNUSED, int offset = UNUSED)} {} {
    code {//
        type |= TOPLEVEL::type::Write;
        collect_data(synth, value, action, type, control, npart, kititem, engine, TOPLEVEL::insert::filterGroup, parameter, offset);} {}
  }
  Function {fetchData(float value, int control, int part, int kititem = UNUSED, int engine = UNUSED, int insert = UNUSED, int parameter = UNUSED, int offset = UNUSED, int miscmsg = UNUSED, int request = UNUSED)} {return_type float
  } {
    code {//
        return collect_readData(synth, value, control, part, kititem, engine, insert, parameter, offset, miscmsg, request);} {}
  }
  Function {returns_update(CommandBlock *getData)} {} {
    code {//
    float value = getData->data.value;
    unsigned char control = getData->data.control;
    unsigned char part = getData->data.part;
    unsigned char kititem = getData->data.kit;
    unsigned char eng = getData->data.engine;
    int value_int = lrint(value);

    bool fromUs = ((getData->data.source & TOPLEVEL::action::noAction) == TOPLEVEL::action::fromGUI);

    if (part != npart || (kititem != EFFECT::type::dynFilter && eng != engine))
        return;
    if (kititem == EFFECT::type::dynFilter)
    {
        //cout << "Dyn filter seen part " << int(part)<< endl;
        if (part == TOPLEVEL::section::systemEffects)
            synth->getGuiMaster()->syseffectui->UpdatePresetColour(1, EFFECT::type::dynFilter - EFFECT::type::none);
        else if (part == TOPLEVEL::section::insertEffects)
            synth->getGuiMaster()->inseffectui->UpdatePresetColour(1, EFFECT::type::dynFilter - EFFECT::type::none);
        else
           synth->getGuiMaster()->partui->inseffectui->UpdatePresetColour(1, EFFECT::type::dynFilter - EFFECT::type::none); // normal part effect
    }

    qdial->activate();
    switch(control)
    {
        case FILTERINSERT::control::centerFrequency:
            cfreqdial->value(value);
            break;

        case FILTERINSERT::control::Q:
            qdial->value(value);
            formantfiltergraph->redraw();
            break;

        case FILTERINSERT::control::frequencyTracking:
            freqtrdial->value(value);
            break;

        case FILTERINSERT::control::velocitySensitivity:
            vsnsadial->value(value);
            if (velsnsamp != NULL)
                *velsnsamp = value_int;
            break;

        case FILTERINSERT::control::velocityCurve:
            vsnsdial->value(value);
            if (velsns != NULL)
                *velsns = value_int;
            break;

        case FILTERINSERT::control::gain:
            gaindial->value(value);
            formantfiltergraph->redraw();
            break;

        case FILTERINSERT::control::stages:
            if (!fromUs)
                stcounter->value(value_int + 1);
            formantfiltergraph->redraw();
            break;

        case FILTERINSERT::control::baseType:
            filtertype->value(value_int);
            cfreqdial->setValueType(getFilterFreqType(value_int));
            updateVCforQ();
            refresh();
            break;

        case FILTERINSERT::control::analogType:
            analogfiltertypechoice->value(value_int);
            if (value_int < 2)
                qdial->deactivate();
            updateVCforQ();
            break;

        case FILTERINSERT::control::stateVariableType:
            svfiltertypechoice->value(value_int);
            break;

        case FILTERINSERT::control::frequencyTrackingRange:
            freqtrackoffset->value(value != 0);
            freqtrdial->setValueType(getFilterFreqTrackType(value != 0));
            break;

        case FILTERINSERT::control::formantSlowness:
            frsldial->value(value);
            break;

        case FILTERINSERT::control::formantClearness:
            wvknob->value(value);
            break;

        case FILTERINSERT::control::formantFrequency:
            formant_freq_dial->value(value);
            formantfiltergraph->redraw();
            break;

        case FILTERINSERT::control::formantQ:
            formant_q_dial->value(value);
            formantfiltergraph->redraw();
            break;

        case FILTERINSERT::control::formantAmplitude:
            formant_amp_dial->value(value);
            formantfiltergraph->redraw();
            break;

        case FILTERINSERT::control::formantStretch:
            strchdial->value(value);
            break;

        case FILTERINSERT::control::formantCenter:
            cfknob->value(value);
            centerfreqvo->do_callback();
            formantfiltergraph->redraw();
            break;

        case FILTERINSERT::control::formantOctave:
            octknob->value(value);
            octavesfreqvo->do_callback();
            formantfiltergraph->redraw();
            break;

        case FILTERINSERT::control::numberOfFormants:
            formantcount->value(value_int);
            update_formant_window();
            formantfiltergraph->redraw();
            break;

        case FILTERINSERT::control::vowelNumber:
            nvowel = value_int;
            update_formant_window();
            formantfiltergraph->redraw();
            break;

        case FILTERINSERT::control::formantNumber:
            nformant = value_int;
            update_formant_window();
            formantfiltergraph->redraw();
            break;

        case FILTERINSERT::control::sequenceSize:
            seqsize->value(value_int);
            update_formant_window();
            break;

        case FILTERINSERT::control::sequencePosition:
            update_formant_window();
            break;

        case FILTERINSERT::control::vowelPositionInSequence:
            vowel_counter->value(value_int);
            break;

        case FILTERINSERT::control::negateInput:
            neginput->value(value != 0);
            break;
    }} {}
  }
  Function {init(FilterParams *filterpars_,unsigned char *velsnsamp_,unsigned char *velsns_, int npart_, int kititem_, int engine_)} {} {
    code {//
    pars=filterpars_;
    synth = pars->getSynthEngine();
    velsnsamp = velsnsamp_;
    velsns = velsns_;
    npart = npart_;
    kititem = kititem_;
    engine = engine_;
    lastformantX = 0;
    lastformantY = 0;
    lastformantW = 0;
    formantSeen = false;
    make_window();
    end();
    make_formant_window();
    filterui->resize(this->x(), this->y(), this->w(), this->h());

    if (velsnsamp == NULL)
    {
        vsnsadial->deactivate();
        vsnsadial->value(127);
    }
    else
        vsnsadial->value(*velsnsamp);

    if (velsns == NULL)
    {
        vsnsdial->deactivate();
        vsnsdial->value(127);
    }
    else
        vsnsdial->value(*velsns);
    cfreqdial->setValueType(getFilterFreqType(pars->Pcategory));
    updateVCforQ();

    analogfiltertypechoice->value(0);
    svfiltertypechoice->value(0);
    stcounter->value(fetchData(0, FILTERINSERT::control::stages, npart, kititem, engine, TOPLEVEL::insert::filterGroup) + 1);
    formantparswindow->label(this->label());
    refresh();} {}
  }
  Function {updateVCforQ()} {return_type void
  } {
    code {//
    switch (filtertype->value())
    {
        case 0:
            if (analogfiltertypechoice->value()<2)
                qdial->setValueType(VC_FilterQAnalogUnused);
            else
                qdial->setValueType(VC_FilterQ);
            break;
        case 1:
        case 2:
            qdial->setValueType(VC_FilterQ);
    }} {}
  }
  decl {FormantFilterGraph *formantfiltergraph;} {public local
  }
  decl {FilterParams *pars;} {private local
  }
  decl {unsigned char *velsnsamp,*velsns;} {private local
  }
  decl {int nvowel,nformant,nseqpos;} {private local
  }
  decl {SynthEngine *synth;} {private local
  }
  decl {int npart;} {private local
  }
  decl {int kititem;} {private local
  }
  decl {int engine;} {private local
  }
  decl {int formantDW;} {private local
  }
  decl {int formantDH;} {private local
  }
  decl {int lastformantX;} {private local
  }
  decl {int lastformantY;} {private local
  }
  decl {int lastformantW;} {private local
  }
  decl {bool formantSeen;} {private local
  }
}
