zynaddsubfx

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

FormantFilterGraph.cpp (9430B)


      1 /*
      2   ZynAddSubFX - a software synthesizer
      3 
      4   FormantFilterGraph.cpp - OSC Formant Filter Graph View
      5   Copyright (C) 2016 Mark McCurry
      6 
      7   This program is free software; you can redistribute it and/or
      8   modify it under the terms of the GNU General Public License
      9   as published by the Free Software Foundation; either version 2
     10   of the License, or (at your option) any later version.
     11 */
     12 #include "FormantFilterGraph.H"
     13 #include <cmath>
     14 #include <cstdio>
     15 #include <cstdlib>
     16 
     17 FormantFilterGraph::FormantFilterGraph(int x,int y, int w, int h, const char *label)
     18 :Fl_Box(x,y,w,h,label), Fl_Osc_Widget(this)
     19 {
     20     memset(Pvowels, 0, sizeof(Pvowels));
     21     Pnumformants = 0;
     22     Pstages = 0;
     23     Pgain = 0;
     24     Pcenterfreq = 0;
     25     Pq = 0;
     26     Poctavesfreq = 0;
     27     nvowel=NULL;
     28     nformant=NULL;
     29     graphpoints=NULL;
     30 }
     31 
     32 void FormantFilterGraph::init(int *nvowel_,int *nformant_)
     33 {
     34     nvowel=nvowel_;
     35     nformant=nformant_;
     36     graphpoints=new float [w()];
     37 
     38     oscRegister("Pvowels");
     39     oscRegister("Pnumformants");
     40     oscRegister("Pstages");
     41     oscRegister("Pcenterfreq");
     42     oscRegister("Poctavesfreq");
     43     oscRegister("Pgain");
     44     oscRegister("Pq");
     45 }
     46 
     47 void FormantFilterGraph::OSC_value(int x, const char *loc)
     48 {
     49     if(strstr(loc, "Pnumformants"))
     50         Pnumformants = x;
     51     else if(strstr(loc, "Pstages"))
     52         Pstages = x;
     53     else if(strstr(loc, "Pcenterfreq"))
     54         Pcenterfreq = x;
     55     else if(strstr(loc, "Pgain"))
     56         Pgain = x;
     57     else if(strstr(loc, "Pq"))
     58         Pq = x;
     59     else if(strstr(loc, "Poctavesfreq"))
     60         Poctavesfreq = x;
     61 
     62     redraw();
     63 }
     64 void FormantFilterGraph::OSC_value(unsigned x, void *v)
     65 {
     66     assert(x == sizeof(Pvowels));
     67     memcpy(&Pvowels[0], v, x);
     68     redraw();
     69 }
     70 
     71 void FormantFilterGraph::draw_freq_line(float freq,int type)
     72 {
     73     const float freqx=getfreqpos(freq);
     74     switch(type){
     75         case 0:fl_line_style(FL_SOLID);break;
     76         case 1:fl_line_style(FL_DOT);break;
     77         case 2:fl_line_style(FL_DASH);break;
     78     };
     79 
     80 
     81     if ((freqx>0.0)&&(freqx<1.0))
     82         fl_line(x()+(int) (freqx*w()),y(),
     83                 x()+(int) (freqx*w()),y()+h());
     84 }
     85 
     86 void FormantFilterGraph::update(void)
     87 {
     88     oscWrite("Pvowels");
     89     oscWrite("Pnumformants");
     90     oscWrite("Pstages");
     91     oscWrite("Pcenterfreq");
     92     oscWrite("Poctavesfreq");
     93     oscWrite("Pgain");
     94     oscWrite("Pq");
     95 }
     96 
     97 void FormantFilterGraph::rebase(std::string new_base)
     98 {
     99     osc->renameLink(loc+"Pvowels", new_base+"Pvowels", this);
    100     osc->renameLink(loc+"Pnumformants", new_base+"Pnumformants", this);
    101     osc->renameLink(loc+"Pstages", new_base+"Pstages", this);
    102     osc->renameLink(loc+"Pcenterfreq", new_base+"Pcenterfreq", this);
    103     osc->renameLink(loc+"Poctavesfreq", new_base+"Poctavesfreq", this);
    104     osc->renameLink(loc+"Pgain", new_base+"Pgain", this);
    105     osc->renameLink(loc+"Pq", new_base+"Pq", this);
    106     loc = new_base;
    107     update();
    108 }
    109 
    110 //TODO A good portion of this is copy/pasta from EnvelopUI's widget
    111 //     REFACTOR!
    112 void FormantFilterGraph::draw()
    113 {
    114     const int maxdB=30;
    115     const int ox=x(),oy=y(),lx=w(),ly=h();
    116 
    117     fl_color(FL_BLACK);
    118     fl_rectf(ox,oy,lx,ly);
    119 
    120 
    121     //draw the lines
    122     fl_color(FL_GRAY);
    123 
    124     fl_line_style(FL_SOLID);
    125     //fl_line(ox+2,oy+ly/2,ox+lx-2,oy+ly/2);
    126 
    127     const float freqx = getfreqpos(1000.0);
    128     if ((freqx>0.0)&&(freqx<1.0))
    129         fl_line(ox+(int) (freqx*lx),oy,
    130                 ox+(int) (freqx*lx),oy+ly);
    131 
    132     for(int i=1;i<10;i++){
    133         if(i==1){
    134             draw_freq_line(i*100.0,0);
    135             draw_freq_line(i*1000.0,0);
    136         }else
    137             if (i==5){
    138                 draw_freq_line(i*100.0,2);
    139                 draw_freq_line(i*1000.0,2);
    140             }else{
    141                 draw_freq_line(i*100.0,1);
    142                 draw_freq_line(i*1000.0,1);
    143             };
    144     };
    145 
    146     draw_freq_line(10000.0,0);
    147     draw_freq_line(20000.0,1);
    148 
    149     fl_line_style(FL_DOT);
    150     int GY=10;if (ly<GY*3) GY=-1;
    151     for (int i=1;i<GY;i++){
    152         int tmp=(int)(ly/(float)GY*i);
    153         fl_line(ox+2,oy+tmp,ox+lx-2,oy+tmp);
    154     };
    155 
    156     fl_color(FL_YELLOW);
    157     fl_font(FL_HELVETICA,10);
    158     if (*nformant < Pnumformants){
    159         draw_freq_line(getformantfreq(Pvowels[*nvowel].formants[*nformant].freq),2);
    160 
    161         //show some information (like current formant frequency,amplitude)
    162         char tmpstr[20];
    163 
    164         snprintf(tmpstr,20,"%.2f kHz",getformantfreq(Pvowels[*nvowel].formants[*nformant].freq)*0.001);
    165         fl_draw(tmpstr,ox+1,oy+1,40,12,FL_ALIGN_LEFT,NULL,0);
    166 
    167         snprintf(tmpstr,20,"%d dB",(int)( rap2dB(1e-9 +
    168                         getformantamp(Pvowels[*nvowel].formants[*nformant].amp))
    169                     + getgain()));
    170         fl_draw(tmpstr,ox+1,oy+15,40,12,FL_ALIGN_LEFT,NULL,0);
    171 
    172     };
    173 
    174     //draw the data
    175 
    176     fl_color(FL_RED);
    177     fl_line_style(FL_SOLID);
    178 
    179     formantfilterH(*nvowel,lx,graphpoints);
    180 
    181     fl_line_style( FL_SOLID, 2 );
    182     fl_begin_line();
    183     int oiy=(int) ((graphpoints[0]/maxdB+1.0)*ly/2.0);
    184     for(int i=1;i<lx;i++){
    185         double iy= ((graphpoints[i]/maxdB+1.0)*ly/2.0);
    186         if ((iy>=0)&&(oiy>=0)&&(iy<ly)&&(oiy<lx))
    187             fl_vertex(ox+i,oy+ly-iy);
    188         oiy=iy;
    189     };
    190     fl_end_line();
    191     fl_line_style(FL_SOLID,0);
    192 }
    193 
    194 FormantFilterGraph::~FormantFilterGraph(void)
    195 {
    196     delete [] graphpoints;
    197 }
    198 
    199 /*
    200  * Parameter control
    201  */
    202 float FormantFilterGraph::getgain()
    203 {
    204     return (Pgain / 64.0f - 1.0f) * 30.0f; //-30..30dB
    205 }
    206 
    207 float FormantFilterGraph::getq()
    208 {
    209     return expf(powf((float) Pq / 127.0f, 2) * logf(1000.0f)) - 0.9f;
    210 }
    211 
    212 /*
    213  * Get the center frequency of the formant's graph
    214  */
    215 float FormantFilterGraph::getcenterfreq()
    216 {
    217     return 10000.0f * powf(10, -(1.0f - Pcenterfreq / 127.0f) * 2.0f);
    218 }
    219 
    220 /*
    221  * Get the number of octave that the formant functions applies to
    222  */
    223 float FormantFilterGraph::getoctavesfreq()
    224 {
    225     return 0.25f + 10.0f * Poctavesfreq / 127.0f;
    226 }
    227 
    228 /*
    229  * Get the frequency from x, where x is [0..1]
    230  */
    231 float FormantFilterGraph::getfreqx(float x)
    232 {
    233     if(x > 1.0f)
    234         x = 1.0f;
    235     float octf = powf(2.0f, getoctavesfreq());
    236     return getcenterfreq() / sqrt(octf) * powf(octf, x);
    237 }
    238 
    239 /*
    240  * Get the x coordinate from frequency (used by the UI)
    241  */
    242 float FormantFilterGraph::getfreqpos(float freq)
    243 {
    244     return (logf(freq) - logf(getfreqx(0.0f))) / logf(2.0f) / getoctavesfreq();
    245 }
    246 
    247 
    248 /*
    249  * Get the freq. response of the formant filter
    250  */
    251 void FormantFilterGraph::formantfilterH(int nvowel, int nfreqs, float *freqs)
    252 {
    253     float c[3], d[3];
    254 
    255     for(int i = 0; i < nfreqs; ++i)
    256         freqs[i] = 0.0f;
    257 
    258     //for each formant...
    259     for(int nformant = 0; nformant < Pnumformants; ++nformant) {
    260         //compute formant parameters(frequency,amplitude,etc.)
    261         const float filter_freq = getformantfreq(Pvowels[nvowel].formants[nformant].freq);
    262         float filter_q    = getformantq(Pvowels[nvowel].formants[nformant].q) * getq();
    263         if(Pstages > 0)
    264             filter_q = (filter_q > 1.0f ? powf(filter_q, 1.0f / (Pstages + 1)) : filter_q);
    265 
    266         const float filter_amp = getformantamp(Pvowels[nvowel].formants[nformant].amp);
    267 
    268         //printf("NFORMANT %d\n", nformant);
    269         //printf("CHARACTERISTICS: FREQ %f Q %f AMP %f\n", filter_freq, filter_q, filter_amp);
    270         const float SampleRate = 48000.0f;
    271 
    272 
    273         if(filter_freq <= (SampleRate / 2 - 100.0f)) {
    274             const float omega = 2 * PI * filter_freq / SampleRate;
    275             const float sn    = sinf(omega);
    276             const float cs    = cosf(omega);
    277             const float alpha = sn / (2 * filter_q);
    278             const float tmp   = 1 + alpha;
    279             c[0] = alpha / tmp *sqrt(filter_q + 1);
    280             c[1] = 0;
    281             c[2] = -alpha / tmp *sqrt(filter_q + 1);
    282             d[1] = -2 * cs / tmp * (-1);
    283             d[2] = (1 - alpha) / tmp * (-1);
    284         }
    285         else
    286             continue;
    287 
    288 
    289         for(int i = 0; i < nfreqs; ++i) {
    290             const float freq = getfreqx(i / (float) nfreqs);
    291 
    292             //Discard frequencies above nyquist rate
    293             if(freq > SampleRate / 2) {
    294                 for(int tmp = i; tmp < nfreqs; ++tmp)
    295                     freqs[tmp] = 0.0f;
    296                 break;
    297             }
    298 
    299             //Convert to normalized frequency
    300             const float fr = freq / SampleRate * PI * 2.0f;
    301 
    302             //Evaluate Complex domain ratio
    303             float x  = c[0], y = 0.0f;
    304             for(int n = 1; n < 3; ++n) {
    305                 x += cosf(n * fr) * c[n];
    306                 y -= sinf(n * fr) * c[n];
    307             }
    308             float h = x * x + y * y;
    309             x = 1.0f;
    310             y = 0.0f;
    311             for(int n = 1; n < 3; ++n) {
    312                 x -= cosf(n * fr) * d[n];
    313                 y += sinf(n * fr) * d[n];
    314             }
    315             h = h / (x * x + y * y);
    316 
    317             freqs[i] += powf(h, (Pstages + 1.0f) / 2.0f) * filter_amp;
    318         }
    319     }
    320 
    321     //Convert to logarithmic data ignoring points that are too small
    322     for(int i = 0; i < nfreqs; ++i) {
    323         if(freqs[i] > 0.000000001f)
    324             freqs[i] = rap2dB(freqs[i]) + getgain();
    325         else
    326             freqs[i] = -90.0f;
    327     }
    328 }
    329 
    330 /*
    331  * Transforms a parameter to the real value
    332  */
    333 float FormantFilterGraph::getformantfreq(unsigned char freq)
    334 {
    335     return getfreqx(freq / 127.0f);
    336 }
    337 
    338 float FormantFilterGraph::getformantamp(unsigned char amp)
    339 {
    340     return powf(0.1f, (1.0f - amp / 127.0f) * 4.0f);
    341 }
    342 
    343 float FormantFilterGraph::getformantq(unsigned char q)
    344 {
    345     return powf(25.0f, (q - 32.0f) / 64.0f);
    346 }