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 }