PageRenderTime 27ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/UI/FormantFilterGraph.cpp

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