echo_monitor.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. /*
  2. * SpanDSP - a series of DSP components for telephony
  3. *
  4. * echo_monitor.cpp - Display echo canceller status, using the FLTK toolkit.
  5. *
  6. * Written by Steve Underwood <steveu@coppice.org>
  7. *
  8. * Copyright (C) 2004 Steve Underwood
  9. *
  10. * All rights reserved.
  11. *
  12. * This program is free software; you can redistribute it and/or modify
  13. * it under the terms of the GNU General Public License version 2, as
  14. * published by the Free Software Foundation.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this program; if not, write to the Free Software
  23. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  24. */
  25. #if defined(HAVE_CONFIG_H)
  26. #include "config.h"
  27. #endif
  28. #if defined(HAVE_FL_FL_H) && defined(HAVE_FL_FL_CARTESIAN_H)
  29. #define __STDC_LIMIT_MACROS
  30. #include <inttypes.h>
  31. #include <stdio.h>
  32. #include <math.h>
  33. #include <stdlib.h>
  34. #include <string.h>
  35. #include <unistd.h>
  36. #include <sys/select.h>
  37. #if defined(HAVE_FFTW3_H)
  38. #include <fftw3.h>
  39. #else
  40. #include <fftw.h>
  41. #endif
  42. #include <FL/Fl.H>
  43. #include <FL/Fl_Overlay_Window.H>
  44. #include <FL/Fl_Light_Button.H>
  45. #include <FL/Fl_Cartesian.H>
  46. #include <FL/Fl_Audio_Meter.H>
  47. #include <FL/fl_draw.H>
  48. #include "spandsp.h"
  49. #include "echo_monitor.h"
  50. struct line_model_monitor_s
  51. {
  52. Fl_Double_Window *w;
  53. Fl_Audio_Meter *audio_meter;
  54. Fl_Group *c_spec;
  55. Fl_Group *c_right;
  56. Fl_Group *c_can;
  57. Fl_Group *c_line_model;
  58. Ca_Canvas *canvas_spec;
  59. Ca_X_Axis *spec_freq;
  60. Ca_Y_Axis *spec_amp;
  61. Ca_Line *spec_re;
  62. double spec_re_plot[2*512];
  63. Ca_Canvas *canvas_can;
  64. Ca_X_Axis *can_x;
  65. Ca_Y_Axis *can_y;
  66. Ca_Line *can_re;
  67. double can_re_plot[512];
  68. Ca_Canvas *canvas_line_model;
  69. Ca_X_Axis *line_model_x;
  70. Ca_Y_Axis *line_model_y;
  71. Ca_Line *line_model_re;
  72. double line_model_re_plot[512];
  73. int in_ptr;
  74. #if defined(HAVE_FFTW3_H)
  75. double in[1024][2];
  76. double out[1024][2];
  77. #else
  78. fftw_complex in[1024];
  79. fftw_complex out[1024];
  80. #endif
  81. fftw_plan p;
  82. };
  83. static int skip = 0;
  84. static struct line_model_monitor_s echo;
  85. static struct line_model_monitor_s *s = &echo;
  86. int echo_can_monitor_can_update(const int16_t *coeffs, int len)
  87. {
  88. int i;
  89. float min;
  90. float max;
  91. if (s->can_re)
  92. delete s->can_re;
  93. s->canvas_can->current(s->canvas_can);
  94. i = 0;
  95. min = coeffs[i];
  96. max = coeffs[i];
  97. for (i = 0; i < len; i++)
  98. {
  99. s->can_re_plot[2*i] = i;
  100. s->can_re_plot[2*i + 1] = coeffs[i];
  101. if (min > coeffs[i])
  102. min = coeffs[i];
  103. if (max < coeffs[i])
  104. max = coeffs[i];
  105. }
  106. s->can_y->maximum((max == min) ? max + 0.2 : max);
  107. s->can_y->minimum(min);
  108. s->can_re = new Ca_Line(len, s->can_re_plot, 0, 0, FL_BLUE, CA_NO_POINT);
  109. if (++skip >= 100)
  110. {
  111. skip = 0;
  112. Fl::check();
  113. }
  114. return 0;
  115. }
  116. /*- End of function --------------------------------------------------------*/
  117. int echo_can_monitor_line_model_update(const int32_t *coeffs, int len)
  118. {
  119. int i;
  120. float min;
  121. float max;
  122. if (s->line_model_re)
  123. delete s->line_model_re;
  124. s->canvas_line_model->current(s->canvas_line_model);
  125. i = 0;
  126. min = coeffs[i];
  127. max = coeffs[i];
  128. for (i = 0; i < len; i++)
  129. {
  130. s->line_model_re_plot[2*i] = i;
  131. s->line_model_re_plot[2*i + 1] = coeffs[i];
  132. if (min > coeffs[i])
  133. min = coeffs[i];
  134. if (max < coeffs[i])
  135. max = coeffs[i];
  136. }
  137. s->line_model_y->maximum((max == min) ? max + 0.2 : max);
  138. s->line_model_y->minimum(min);
  139. s->line_model_re = new Ca_Line(len, s->line_model_re_plot, 0, 0, FL_BLUE, CA_NO_POINT);
  140. if (++skip >= 100)
  141. {
  142. skip = 0;
  143. Fl::check();
  144. }
  145. return 0;
  146. }
  147. /*- End of function --------------------------------------------------------*/
  148. int echo_can_monitor_line_spectrum_update(const int16_t amp[], int len)
  149. {
  150. int i;
  151. int x;
  152. for (i = 0; i < len; i++)
  153. s->audio_meter->sample(amp[i]/32768.0);
  154. if (s->in_ptr + len < 512)
  155. {
  156. /* Just add this fragment to the buffer. */
  157. for (i = 0; i < len; i++)
  158. #if defined(HAVE_FFTW3_H)
  159. s->in[s->in_ptr + i][0] = amp[i];
  160. #else
  161. s->in[s->in_ptr + i].re = amp[i];
  162. #endif
  163. s->in_ptr += len;
  164. return 0;
  165. }
  166. if (len >= 512)
  167. {
  168. /* We have enough for a whole block. Use the last 512 samples
  169. we have. */
  170. x = len - 512;
  171. for (i = 0; i < 512; i++)
  172. #if defined(HAVE_FFTW3_H)
  173. s->in[i][0] = amp[x + i];
  174. #else
  175. s->in[i].re = amp[x + i];
  176. #endif
  177. }
  178. else
  179. {
  180. /* We want the last 512 samples. */
  181. x = 512 - len;
  182. for (i = 0; i < x; i++)
  183. #if defined(HAVE_FFTW3_H)
  184. s->in[i][0] = s->in[s->in_ptr - x + i][0];
  185. #else
  186. s->in[i].re = s->in[s->in_ptr - x + i].re;
  187. #endif
  188. for (i = x; i < 512; i++)
  189. #if defined(HAVE_FFTW3_H)
  190. s->in[i][0] = amp[i - x];
  191. #else
  192. s->in[i].re = amp[i - x];
  193. #endif
  194. }
  195. s->in_ptr = 0;
  196. #if defined(HAVE_FFTW3_H)
  197. fftw_execute(s->p);
  198. #else
  199. fftw_one(s->p, s->in, s->out);
  200. #endif
  201. if (s->spec_re)
  202. delete s->spec_re;
  203. s->canvas_spec->current(s->canvas_spec);
  204. for (i = 0; i < 512; i++)
  205. {
  206. s->spec_re_plot[2*i] = i*4000.0/512.0;
  207. #if defined(HAVE_FFTW3_H)
  208. s->spec_re_plot[2*i + 1] = 10.0*log10((s->out[i][0]*s->out[i][0] + s->out[i][1]*s->out[i][1])/(256.0*32768*256.0*32768) + 1.0e-10) + 3.14;
  209. #else
  210. s->spec_re_plot[2*i + 1] = 10.0*log10((s->out[i].re*s->out[i].re + s->out[i].im*s->out[i].im)/(256.0*32768*256.0*32768) + 1.0e-10) + 3.14;
  211. #endif
  212. }
  213. s->spec_re = new Ca_Line(512, s->spec_re_plot, 0, 0, FL_BLUE, CA_NO_POINT);
  214. Fl::check();
  215. return 0;
  216. }
  217. /*- End of function --------------------------------------------------------*/
  218. int start_echo_can_monitor(int len)
  219. {
  220. char buf[132 + 1];
  221. float x;
  222. float y;
  223. int i;
  224. s->w = new Fl_Double_Window(850, 400, "Echo canceller monitor");
  225. s->c_spec = new Fl_Group(0, 0, 380, 400);
  226. s->c_spec->box(FL_DOWN_BOX);
  227. s->c_spec->align(FL_ALIGN_TOP | FL_ALIGN_INSIDE);
  228. s->canvas_spec = new Ca_Canvas(60, 30, 300, 300, "Spectrum");
  229. s->canvas_spec->box(FL_PLASTIC_DOWN_BOX);
  230. s->canvas_spec->color(7);
  231. s->canvas_spec->align(FL_ALIGN_TOP);
  232. s->canvas_spec->border(15);
  233. s->spec_freq = new Ca_X_Axis(65, 330, 290, 30, "Freq (Hz)");
  234. s->spec_freq->align(FL_ALIGN_BOTTOM);
  235. s->spec_freq->minimum(0);
  236. s->spec_freq->maximum(4000);
  237. s->spec_freq->label_format("%g");
  238. s->spec_freq->minor_grid_color(fl_gray_ramp(20));
  239. s->spec_freq->major_grid_color(fl_gray_ramp(15));
  240. s->spec_freq->label_grid_color(fl_gray_ramp(10));
  241. s->spec_freq->grid_visible(CA_LABEL_GRID | CA_ALWAYS_VISIBLE);
  242. s->spec_freq->minor_grid_style(FL_DOT);
  243. s->spec_freq->major_step(5);
  244. s->spec_freq->label_step(1);
  245. s->spec_freq->axis_color(FL_BLACK);
  246. s->spec_freq->axis_align(CA_BOTTOM | CA_LINE);
  247. s->spec_amp = new Ca_Y_Axis(20, 35, 40, 290, "Amp (dBmO)");
  248. s->spec_amp->align(FL_ALIGN_LEFT);
  249. s->spec_amp->minimum(-80.0);
  250. s->spec_amp->maximum(10.0);
  251. s->spec_amp->minor_grid_color(fl_gray_ramp(20));
  252. s->spec_amp->major_grid_color(fl_gray_ramp(15));
  253. s->spec_amp->label_grid_color(fl_gray_ramp(10));
  254. //s->spec_amp->grid_visible(CA_MINOR_TICK | CA_MAJOR_TICK | CA_LABEL_GRID | CA_ALWAYS_VISIBLE);
  255. s->spec_amp->grid_visible(CA_LABEL_GRID | CA_ALWAYS_VISIBLE);
  256. s->spec_amp->minor_grid_style(FL_DOT);
  257. s->spec_amp->major_step(5);
  258. s->spec_amp->label_step(1);
  259. s->spec_amp->axis_color(FL_BLACK);
  260. s->spec_amp->current();
  261. s->spec_re = NULL;
  262. s->c_spec->end();
  263. s->c_right = new Fl_Group(440, 0, 465, 405);
  264. s->c_can = new Fl_Group(380, 0, 415, 200);
  265. s->c_can->box(FL_DOWN_BOX);
  266. s->c_can->align(FL_ALIGN_TOP | FL_ALIGN_INSIDE);
  267. s->c_can->current();
  268. s->canvas_can = new Ca_Canvas(460, 35, 300, 100, "Canceller coefficients");
  269. s->canvas_can->box(FL_PLASTIC_DOWN_BOX);
  270. s->canvas_can->color(7);
  271. s->canvas_can->align(FL_ALIGN_TOP);
  272. Fl_Group::current()->resizable(s->canvas_can);
  273. s->canvas_can->border(15);
  274. s->can_x = new Ca_X_Axis(465, 135, 290, 30, "Tap");
  275. s->can_x->align(FL_ALIGN_BOTTOM);
  276. s->can_x->minimum(0.0);
  277. s->can_x->maximum((float) len);
  278. s->can_x->label_format("%g");
  279. s->can_x->minor_grid_color(fl_gray_ramp(20));
  280. s->can_x->major_grid_color(fl_gray_ramp(15));
  281. s->can_x->label_grid_color(fl_gray_ramp(10));
  282. s->can_x->grid_visible(CA_LABEL_GRID | CA_ALWAYS_VISIBLE);
  283. s->can_x->minor_grid_style(FL_DOT);
  284. s->can_x->major_step(5);
  285. s->can_x->label_step(1);
  286. s->can_x->axis_align(CA_BOTTOM | CA_LINE);
  287. s->can_x->axis_color(FL_BLACK);
  288. s->can_x->current();
  289. s->can_y = new Ca_Y_Axis(420, 40, 40, 90, "Amp");
  290. s->can_y->align(FL_ALIGN_LEFT);
  291. s->can_y->minimum(-0.1);
  292. s->can_y->maximum(0.1);
  293. s->can_y->minor_grid_color(fl_gray_ramp(20));
  294. s->can_y->major_grid_color(fl_gray_ramp(15));
  295. s->can_y->label_grid_color(fl_gray_ramp(10));
  296. s->can_y->grid_visible(CA_LABEL_GRID | CA_ALWAYS_VISIBLE);
  297. s->can_y->minor_grid_style(FL_DOT);
  298. s->can_y->major_step(5);
  299. s->can_y->label_step(1);
  300. s->can_y->axis_color(FL_BLACK);
  301. s->can_y->current();
  302. s->c_can->end();
  303. s->can_re = NULL;
  304. s->c_line_model = new Fl_Group(380, 200, 415, 200);
  305. s->c_line_model->box(FL_DOWN_BOX);
  306. s->c_line_model->align(FL_ALIGN_TOP | FL_ALIGN_INSIDE);
  307. s->c_line_model->current();
  308. s->canvas_line_model = new Ca_Canvas(460, 235, 300, 100, "Line impulse response model");
  309. s->canvas_line_model->box(FL_PLASTIC_DOWN_BOX);
  310. s->canvas_line_model->color(7);
  311. s->canvas_line_model->align(FL_ALIGN_TOP);
  312. Fl_Group::current()->resizable(s->canvas_line_model);
  313. s->canvas_line_model->border(15);
  314. s->line_model_x = new Ca_X_Axis(465, 335, 290, 30, "Tap");
  315. s->line_model_x->align(FL_ALIGN_BOTTOM);
  316. s->line_model_x->minimum(0.0);
  317. s->line_model_x->maximum((float) len);
  318. s->line_model_x->label_format("%g");
  319. s->line_model_x->minor_grid_color(fl_gray_ramp(20));
  320. s->line_model_x->major_grid_color(fl_gray_ramp(15));
  321. s->line_model_x->label_grid_color(fl_gray_ramp(10));
  322. s->line_model_x->grid_visible(CA_LABEL_GRID | CA_ALWAYS_VISIBLE);
  323. s->line_model_x->minor_grid_style(FL_DOT);
  324. s->line_model_x->major_step(5);
  325. s->line_model_x->label_step(1);
  326. s->line_model_x->axis_align(CA_BOTTOM | CA_LINE);
  327. s->line_model_x->axis_color(FL_BLACK);
  328. s->line_model_x->current();
  329. s->line_model_y = new Ca_Y_Axis(420, 240, 40, 90, "Amp");
  330. s->line_model_y->align(FL_ALIGN_LEFT);
  331. s->line_model_y->minimum(-0.1);
  332. s->line_model_y->maximum(0.1);
  333. s->line_model_y->minor_grid_color(fl_gray_ramp(20));
  334. s->line_model_y->major_grid_color(fl_gray_ramp(15));
  335. s->line_model_y->label_grid_color(fl_gray_ramp(10));
  336. s->line_model_y->grid_visible(CA_LABEL_GRID | CA_ALWAYS_VISIBLE);
  337. s->line_model_y->minor_grid_style(FL_DOT);
  338. s->line_model_y->major_step(5);
  339. s->line_model_y->label_step(1);
  340. s->line_model_y->axis_color(FL_BLACK);
  341. s->line_model_y->current();
  342. s->c_line_model->end();
  343. s->line_model_re = NULL;
  344. s->audio_meter = new Fl_Audio_Meter(810, 40, 10, 250, "");
  345. s->audio_meter->box(FL_PLASTIC_UP_BOX);
  346. s->audio_meter->type(FL_VERT_AUDIO_METER);
  347. s->c_right->end();
  348. Fl_Group::current()->resizable(s->c_right);
  349. s->w->end();
  350. s->w->show();
  351. #if defined(HAVE_FFTW3_H)
  352. s->p = fftw_plan_dft_1d(1024, s->in, s->out, FFTW_BACKWARD, FFTW_ESTIMATE);
  353. for (i = 0; i < 1024; i++)
  354. {
  355. s->in[i][0] = 0.0;
  356. s->in[i][1] = 0.0;
  357. }
  358. #else
  359. s->p = fftw_create_plan(1024, FFTW_BACKWARD, FFTW_ESTIMATE);
  360. for (i = 0; i < 1024; i++)
  361. {
  362. s->in[i].re = 0.0;
  363. s->in[i].im = 0.0;
  364. }
  365. #endif
  366. s->in_ptr = 0;
  367. Fl::check();
  368. return 0;
  369. }
  370. /*- End of function --------------------------------------------------------*/
  371. void echo_can_monitor_wait_to_end(void)
  372. {
  373. fd_set rfds;
  374. int res;
  375. struct timeval tv;
  376. fprintf(stderr, "Processing complete. Press the <enter> key to end\n");
  377. do
  378. {
  379. usleep(100000);
  380. Fl::check();
  381. FD_ZERO(&rfds);
  382. FD_SET(0, &rfds);
  383. tv.tv_usec = 100000;
  384. tv.tv_sec = 0;
  385. res = select(1, &rfds, NULL, NULL, &tv);
  386. }
  387. while (res <= 0);
  388. }
  389. /*- End of function --------------------------------------------------------*/
  390. void echo_can_monitor_update_display(void)
  391. {
  392. Fl::check();
  393. Fl::check();
  394. Fl::check();
  395. Fl::check();
  396. Fl::check();
  397. Fl::check();
  398. Fl::check();
  399. Fl::check();
  400. Fl::check();
  401. Fl::check();
  402. Fl::check();
  403. Fl::check();
  404. }
  405. /*- End of function --------------------------------------------------------*/
  406. #endif
  407. /*- End of file ------------------------------------------------------------*/