More minor refinement of scales, phase/imp calc, etc.
[xiph/unicode.git] / spectrum / plot.c
blob51c00a58d3d1720bcb2d59a1386ff28ed2818340
1 /*
3 * gt2 spectrum analyzer
4 *
5 * Copyright (C) 2004 Monty
7 * This analyzer is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
10 * any later version.
12 * The analyzer is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Postfish; see the file COPYING. If not, write to the
19 * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "analyzer.h"
25 #include <stdlib.h>
26 #include <math.h>
27 #include <string.h>
28 #include <gdk/gdkkeysyms.h>
29 #include "plot.h"
31 static GtkDrawingAreaClass *parent_class = NULL;
33 static void compute_imp_scale(GtkWidget *widget){
34 Plot *p=PLOT(widget);
35 int height=widget->allocation.height-p->pady;
36 int i;
37 double lfreqs[9]={10000000.,1000000.,100000.,10000.,1000.,100.,10.,1.,.1};
38 double tfreqs[64]={9000000.,8000000.,7000000.,6000000.,
39 5000000.,4000000.,3000000.,2000000.,
40 900000.,800000.,700000.,600000.,
41 500000.,400000.,300000.,200000.,
42 90000.,80000.,70000.,60000.,
43 50000.,40000.,30000.,20000.,
44 9000.,8000.,7000.,6000.,
45 5000.,4000.,3000.,2000.,
46 900.,800.,700.,600.,
47 500.,400.,300.,200.,
48 90.,80.,70.,60.,
49 50.,40.,30.,20,
50 9.,8.,7.,6.,
51 5.,4.,3.,2.,
52 .9,.8,.7,.6,
53 .5,.4,.3,.2};
55 for(i=0;i<9;i++)
56 p->ygrid[i]=rint( (log10(p->disp_ymax)-log10(lfreqs[i]))/(log10(p->disp_ymax)-log10(.1)) * (height-1));
57 for(i=0;i<64;i++)
58 p->ytic[i]=rint( (log10(p->disp_ymax)-log10(tfreqs[i]))/(log10(p->disp_ymax)-log10(.1)) * (height-1));
59 p->ygrids=9;
60 p->ytics=64;
64 static void compute_metadata(GtkWidget *widget){
65 Plot *p=PLOT(widget);
66 int width=widget->allocation.width-p->padx;
67 int i;
69 /* find the places to plot the x grid lines according to scale */
70 switch(p->scale){
71 case 0: /* log */
73 double lfreqs[6]={1.,10.,100.,1000.,10000.,100000};
74 double tfreqs[37]={5.,6.,7.,8.,9.,20.,30.,40.,50.,60.,70.,80.,90.
75 ,200.,300.,400.,500.,600.,700.,800.,900.,
76 2000.,3000.,4000.,5000.,6000.,7000.,8000.,9000.,
77 20000.,30000,40000,50000,60000,70000,80000,90000};
78 for(i=0;i<6;i++)
79 p->xgrid[i]=rint( (log10(lfreqs[i])-log10(5.))/(log10(100000.)-log10(5.)) * (width-1))+p->padx;
80 for(i=0;i<37;i++)
81 p->xtic[i]=rint( (log10(tfreqs[i])-log10(5.))/(log10(100000.)-log10(5.)) * (width-1))+p->padx;
82 p->xgrids=6;
83 p->xtics=37;
86 break;
87 case 1: /* ISO log */
89 double lfreqs[10]={31.,63.,125.,250.,500.,1000.,2000.,4000.,8000.,16000.};
90 double tfreqs[20]={25.,40.,50.,80.,100.,160.,200.,315.,400.,630.,800.,
91 1250.,1600.,2500.,3150.,5000.,6300.,10000.,12500.,20000.};
92 for(i=0;i<10;i++)
93 p->xgrid[i]=rint( (log2(lfreqs[i])-log2(25.))/(log2(20000.)-log2(25.)) * (width-1))+p->padx;
94 for(i=0;i<20;i++)
95 p->xtic[i]=rint( (log2(tfreqs[i])-log2(25.))/(log2(20000.)-log2(25.)) * (width-1))+p->padx;
96 p->xgrids=10;
97 p->xtics=20;
100 break;
101 case 2: /* linear; 2kHz spacing */
103 float lfreq;
104 for(i=0;i<11;i++){
105 lfreq=i*2000.;
106 p->xgrid[i]=rint(lfreq/20000. * (width-1))+p->padx;
109 p->xgrids=11;
110 p->xtics=0;
111 while((lfreq=(p->xtics+1)*500.)<20000.)
112 p->xtic[p->xtics++]=rint(lfreq/20000. * (width-1))+p->padx;
114 break;
120 GdkColor chcolor(int ch){
121 GdkColor rgb={0,0,0,0};
123 switch(ch%7){
124 case 0:
125 rgb.red=0x4000;
126 rgb.green=0x4000;
127 rgb.blue=0x4000;
128 break;
129 case 1:
130 rgb.red=0xd000;
131 rgb.green=0x0000;
132 rgb.blue=0x0000;
133 break;
134 case 2:
135 rgb.red=0x0000;
136 rgb.green=0xb000;
137 rgb.blue=0x0000;
138 break;
139 case 3:
140 rgb.red=0x0000;
141 rgb.green=0x0000;
142 rgb.blue=0xf000;
143 break;
144 case 4:
145 rgb.red=0xc000;
146 rgb.green=0xc000;
147 rgb.blue=0x0000;
148 break;
149 case 5:
150 rgb.red=0x0000;
151 rgb.green=0xc000;
152 rgb.blue=0xc000;
153 break;
154 case 6:
155 rgb.red=0xc000;
156 rgb.green=0x0000;
157 rgb.blue=0xe000;
158 break;
161 return rgb;
164 static void draw(GtkWidget *widget){
165 int i;
166 Plot *p=PLOT(widget);
167 int height=widget->allocation.height;
168 int width=widget->allocation.width;
169 GtkWidget *parent=gtk_widget_get_parent(widget);
170 int impedence = (p->link == LINK_IMPEDENCE_p1 ||
171 p->link == LINK_IMPEDENCE_1 ||
172 p->link == LINK_IMPEDENCE_10);
173 int phase = (p->link == LINK_PHASE);
174 int padx = p->padx;
176 if(phase){
177 /* are any of the phase channels actually active? */
178 int gi;
179 int ch=0;
181 phase = 0;
182 for(gi=0;gi<p->groups && !phase;gi++){
183 if(p->ch_active[ch+1]){
184 phase=1;
185 break;
188 ch+=p->ch[gi];
192 if(!p->drawgc){
193 p->drawgc=gdk_gc_new(p->backing);
194 gdk_gc_copy(p->drawgc,widget->style->black_gc);
196 if(!p->dashes){
197 p->dashes=gdk_gc_new(p->backing);
198 gdk_gc_copy(p->dashes, p->drawgc);
199 gdk_gc_set_line_attributes(p->dashes, 1, GDK_LINE_ON_OFF_DASH, GDK_CAP_BUTT, GDK_JOIN_MITER);
200 gdk_gc_set_dashes(p->dashes,0,"\002\002",2);
203 /* clear the old rectangle out */
205 GdkGC *gc=parent->style->bg_gc[0];
206 gdk_draw_rectangle(p->backing,gc,1,0,0,padx,height);
207 gdk_draw_rectangle(p->backing,gc,1,0,height-p->pady,width,p->pady);
209 gc=parent->style->white_gc;
210 gdk_draw_rectangle(p->backing,gc,1,padx,0,width-padx,height-p->pady+1);
213 /* draw the light x grid */
215 int i;
216 GdkColor rgb={0,0,0,0};
218 rgb.red=0xc000;
219 rgb.green=0xff00;
220 rgb.blue=0xff00;
221 gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
223 for(i=0;i<p->xtics;i++)
224 gdk_draw_line(p->backing,p->drawgc,p->xtic[i],0,p->xtic[i],height-p->pady);
227 /* draw the x labels */
229 PangoLayout **proper;
230 switch(p->scale){
231 case 0:
232 proper=p->log_layout;
233 break;
234 case 1:
235 proper=p->iso_layout;
236 break;
237 case 2:
238 proper=p->lin_layout;
239 break;
241 for(i=0;i<p->xgrids;i++){
242 int px,py;
243 pango_layout_get_pixel_size(proper[i],&px,&py);
245 gdk_draw_layout (p->backing,
246 widget->style->black_gc,
247 p->xgrid[i]-(px/2), height-py+2,
248 proper[i]);
252 /* draw the light y grid */
253 if(impedence){ /* impedence mode */
255 GdkColor rgb={0,0,0,0};
256 rgb.red=0xc000;
257 rgb.green=0xff00;
258 rgb.blue=0xff00;
259 gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
261 compute_imp_scale(widget);
263 for(i=0;i<p->ytics;i++)
264 gdk_draw_line(p->backing,p->drawgc,padx,p->ytic[i],width,p->ytic[i]);
266 }else{
267 float del=(height-p->pady-1)/(float)p->disp_depth,off;
268 int i,half=0;
269 int max,mul;
270 GdkColor rgb={0,0,0,0};
273 if(del>16){
274 half=1;
275 max=303;
276 mul=1;
277 off=(p->disp_ymax-ceil(p->disp_ymax))*2;
278 del*=.5;
279 }else if(del>8){
280 max=151;
281 mul=1;
282 off=p->disp_ymax-ceil(p->disp_ymax);
283 }else if(del*2>8){
284 max=76;
285 mul=2;
286 off=p->disp_ymax-ceil(p->disp_ymax*.5)*2;
287 }else{
288 max=31;
289 mul=5;
290 off=p->disp_ymax-ceil(p->disp_ymax*.2)*5;
293 rgb.red=0xc000;
294 rgb.green=0xff00;
295 rgb.blue=0xff00;
296 gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
297 gdk_gc_set_rgb_fg_color(p->dashes,&rgb);
299 for(i=0;i<max;i+=2){
300 int ymid=rint(del * (i * mul + off));
301 if(ymid>=0 && ymid<height-p->pady)
302 gdk_draw_line(p->backing,p->drawgc,padx,ymid,width,ymid);
305 for(i=1;i<max;i+=2){
306 int ymid=rint(del * (i * mul + off));
307 if(ymid>=0 && ymid<height-p->pady)
308 gdk_draw_line(p->backing,(half?p->dashes:p->drawgc),padx,ymid,width,ymid);
313 /* dark x grid */
315 int i;
316 GdkColor rgb={0,0,0,0};
318 rgb.red=0x0000;
319 rgb.green=0xc000;
320 rgb.blue=0xc000;
321 gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
323 for(i=0;i<p->xgrids;i++)
324 gdk_draw_line(p->backing,p->drawgc,p->xgrid[i],0,p->xgrid[i],height-p->pady);
327 /* dark y grid */
328 if(impedence){
329 GdkColor rgb={0,0,0,0};
330 rgb.red=0x0000;
331 rgb.green=0xc000;
332 rgb.blue=0xc000;
334 gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
336 for(i=0;i<p->ygrids;i++){
337 int px,py;
338 pango_layout_get_pixel_size(p->imp_layout[i],&px,&py);
340 gdk_draw_layout (p->backing,
341 widget->style->black_gc,
342 padx-px-2, p->ygrid[i]-py/2,
343 p->imp_layout[i]);
345 gdk_draw_line(p->backing,p->drawgc,padx,p->ygrid[i],width,p->ygrid[i]);
348 }else{
349 GdkColor rgb={0,0,0,0};
350 int label=ceil(p->disp_ymax/5+28),i;
351 float del=(height-p->pady-1)/(float)p->disp_depth,step;
352 float off=p->disp_ymax-ceil(p->disp_ymax*.2)*5;
353 step=2;
354 if(del>8)step=1;
356 for(i=0;i<32;i++){
357 if(((label-i)&1)==0 || step==1){
358 int ymid=rint(del * (i*5+off));
359 int px,py;
361 if(((label-i)&1)==0){
362 rgb.red=0x0000;
363 rgb.green=0xc000;
364 rgb.blue=0xc000;
365 }else{
366 rgb.red=0xa000;
367 rgb.green=0xe000;
368 rgb.blue=0xe000;
370 gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
372 if(label-i>=0 && label-i<57 && ymid>=0 && ymid<height-p->pady){
373 pango_layout_get_pixel_size(p->db_layout[label-i],&px,&py);
375 gdk_draw_layout (p->backing,
376 widget->style->black_gc,
377 padx-px-2, ymid-py/2,
378 p->db_layout[label-i]);
379 gdk_draw_line(p->backing,p->drawgc,padx,ymid,width,ymid);
385 /* phase? draw in phase and tics on right axis */
386 if(phase){
387 GdkColor rgb={0,0xd000,0x0000,0x0000};
388 float depth = p->disp_pmax-p->disp_pmin;
389 int label=ceil(p->disp_pmax/10+18),i;
390 float del=(height-p->pady-1)/depth,step;
391 float off=p->disp_pmax-ceil(p->disp_pmax*.1)*10;
392 step=2;
393 if(del>8)step=1;
395 gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
396 for(i=0;i<37;i++){
397 if(((label-i)&1)==0 || step==1){
398 int ymid=rint(del * (i*10+off));
399 int px,py;
401 if(label-i>=0 && label-i<37 && ymid>=0 && ymid<height-p->pady){
402 pango_layout_get_pixel_size(p->phase_layout[label-i],&px,&py);
404 gdk_draw_layout (p->backing,p->drawgc,
405 width-p->phax, ymid-py/2,
406 p->phase_layout[label-i]);
411 if(del>10){
412 for(i=0;;i++){
413 int ymid=rint(del * (i+off));
414 if(ymid>=height-p->pady)break;
415 if(ymid>=0)
416 gdk_draw_line(p->backing,p->drawgc,width-p->phax-(i%5==0?15:10),ymid,width-p->phax-(i%5==0?5:7),ymid);
418 }else if(del>5){
419 for(i=0;;i++){
420 int ymid=rint(del * (i*2+off));
421 if(ymid>=height-p->pady)break;
422 if(ymid>=0)
423 gdk_draw_line(p->backing,p->drawgc,width-p->phax-12,ymid,width-p->phax-7,ymid);
425 } else if(del>2){
426 for(i=0;;i++){
427 int ymid=rint(del * (i*5+off));
428 if(ymid>=height-p->pady)break;
429 if(ymid>=0)
430 gdk_draw_line(p->backing,p->drawgc,width-p->phax-15,ymid,width-p->phax-5,ymid);
434 for(i=0;;i++){
435 int ymid=rint(del * (i*10+off));
436 if(ymid>=height-p->pady)break;
437 if(ymid>=0){
438 gdk_draw_line(p->backing,p->drawgc,width-p->phax-5,ymid-1,width-p->phax-2,ymid-1);
439 gdk_draw_line(p->backing,p->drawgc,width-p->phax-25,ymid,width-p->phax-2,ymid);
440 gdk_draw_line(p->backing,p->drawgc,width-p->phax-5,ymid+1,width-p->phax-2,ymid+1);
446 /* draw actual data */
448 int cho=0;
449 int gi;
450 for(gi=0;gi<p->groups;gi++){
451 int ch;
452 GdkColor rgb;
454 for(ch=cho;ch<cho+p->ch[gi];ch++){
455 if(p->ch_active[ch]){
456 int prev;
457 int first=0;
458 float yprev=NAN;
460 rgb = chcolor(ch);
461 gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
463 for(i=0;i<width-padx;i++){
464 float val=p->ydata[ch][i];
465 int y;
467 if(isnan(yprev) || isnan(val)){
468 yprev = val;
469 }else{
470 yprev = val;
472 if(impedence){ /* log scale for impedence */
473 y =rint( (log10(p->disp_ymax)-log10(val))/(log10(p->disp_ymax)-log10(.1)) *
474 (height-p->pady-1));
475 }else if(phase && ch==cho+1){
476 y= rint((height-p->pady-1)/(p->disp_pmax-p->disp_pmin)*(p->disp_pmax-val));
477 }else{
478 y= rint((height-p->pady-1)/p->disp_depth*(p->disp_ymax-val));
481 if(first && (y<height-p->pady || prev<height-p->pady)){
482 int ly = y;
483 int lp = prev;
485 if(ly>=height-p->pady)ly=height-p->pady;
486 if(lp>=height-p->pady)lp=height-p->pady;
488 gdk_draw_line(p->backing,p->drawgc,padx+i-1,lp,padx+i,ly);
490 ly++;
491 lp++;
493 if(ly>=height-p->pady)ly=height-p->pady;
494 if(lp>=height-p->pady)lp=height-p->pady;
496 gdk_draw_line(p->backing,p->drawgc,padx+i-1,lp,padx+i,ly);
498 first=1;
499 prev=y;
506 cho+=p->ch[gi];
511 static void draw_and_expose(GtkWidget *widget){
512 Plot *p=PLOT(widget);
513 if(!GDK_IS_DRAWABLE(p->backing))return;
514 draw(widget);
515 if(!GTK_WIDGET_DRAWABLE(widget))return;
516 if(!GDK_IS_DRAWABLE(widget->window))return;
517 gdk_draw_drawable(widget->window,
518 widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
519 p->backing,
520 0, 0,
521 0, 0,
522 widget->allocation.width,
523 widget->allocation.height);
526 static gboolean expose( GtkWidget *widget, GdkEventExpose *event ){
527 Plot *p=PLOT(widget);
528 gdk_draw_drawable(widget->window,
529 widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
530 p->backing,
531 event->area.x, event->area.y,
532 event->area.x, event->area.y,
533 event->area.width, event->area.height);
535 return FALSE;
538 static void size_request (GtkWidget *widget,GtkRequisition *requisition){
539 Plot *p=PLOT(widget);
540 /* no smaller than 800x600 */
541 requisition->width = 800;
542 requisition->height = 600;
543 int axisy=0,axisx=0,pady=0,padx=0,phax=0,px,py,i;
545 /* find max lin layout */
547 int max=0;
548 for(i=0;p->lin_layout[i];i++){
549 pango_layout_get_pixel_size(p->lin_layout[i],&px,&py);
550 if(px>max)max=px;
551 if(py>pady)pady=py;
553 max*=i;
554 if(axisx<max)axisx=max;
556 /* find max log layout */
558 int max=0;
559 for(i=0;p->log_layout[i];i++){
560 pango_layout_get_pixel_size(p->log_layout[i],&px,&py);
561 if(px>max)max=px;
562 if(py>pady)pady=py;
564 max*=i;
565 if(axisx<max)axisx=max;
567 /* find max iso layout */
569 int max=0;
570 for(i=0;p->iso_layout[i];i++){
571 pango_layout_get_pixel_size(p->iso_layout[i],&px,&py);
572 if(px>max)max=px;
573 if(py>pady)pady=py;
575 max*=i;
576 if(axisx<max)axisx=max;
578 /* find max db layout */
580 int max=0;
581 for(i=0;p->db_layout[i];i++){
582 pango_layout_get_pixel_size(p->db_layout[i],&px,&py);
583 if(py>max)max=py;
584 if(px>padx)padx=px;
586 axisy=(max+5)*8;
587 if(axisy<max)axisx=max;
589 /* find max imped layout */
591 int max=0;
592 for(i=0;p->imp_layout[i];i++){
593 pango_layout_get_pixel_size(p->imp_layout[i],&px,&py);
594 if(py>max)max=py;
595 if(px>padx)padx=px;
597 axisy=(max+5)*8;
598 if(axisy<max)axisx=max;
600 /* find max phase layout */
602 int max=0;
603 for(i=0;p->phase_layout[i];i++){
604 pango_layout_get_pixel_size(p->phase_layout[i],&px,&py);
605 if(py>max)max=py;
606 if(px>phax)phax=px;
608 axisy=(max+5)*8;
609 if(axisy<max)axisx=max;
612 if(requisition->width<axisx+padx)requisition->width=axisx+padx;
613 if(requisition->height<axisy+pady)requisition->height=axisy+pady;
614 p->padx=padx;
615 p->pady=pady;
616 p->phax=phax;
619 static gboolean configure(GtkWidget *widget, GdkEventConfigure *event){
620 Plot *p=PLOT(widget);
621 int i;
623 if (p->backing)
624 g_object_unref(p->backing);
626 p->backing = gdk_pixmap_new(widget->window,
627 widget->allocation.width,
628 widget->allocation.height,
629 -1);
631 if(p->ydata){
632 for(i=0;i<p->total_ch;i++)free(p->ydata[i]);
633 free(p->ydata);
636 p->ydata=malloc(p->total_ch*sizeof(*p->ydata));
637 for(i=0;i<p->total_ch;i++)
638 p->ydata[i]=
639 calloc(widget->allocation.width,sizeof(**p->ydata));
641 compute_metadata(widget);
642 plot_refresh(p,NULL);
643 draw_and_expose(widget);
645 return TRUE;
648 static void plot_class_init (PlotClass *class){
649 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
650 parent_class = g_type_class_peek_parent (class);
652 widget_class->expose_event = expose;
653 widget_class->configure_event = configure;
654 widget_class->size_request = size_request;
657 static void plot_init (Plot *p){
658 p->mode=0;
659 p->scale=0;
660 p->depth=45.;
663 GType plot_get_type (void){
664 static GType m_type = 0;
665 if (!m_type){
666 static const GTypeInfo m_info={
667 sizeof (PlotClass),
668 NULL, /* base_init */
669 NULL, /* base_finalize */
670 (GClassInitFunc) plot_class_init,
671 NULL, /* class_finalize */
672 NULL, /* class_data */
673 sizeof (Plot),
675 (GInstanceInitFunc) plot_init,
679 m_type = g_type_register_static (GTK_TYPE_DRAWING_AREA, "Plot", &m_info, 0);
682 return m_type;
685 GtkWidget* plot_new (int size, int groups, int *channels, int *rate){
686 GtkWidget *ret= GTK_WIDGET (g_object_new (plot_get_type (), NULL));
687 Plot *p=PLOT(ret);
688 int g,i;
689 int ch=0;
691 p->groups = groups;
692 for(g=0;g<groups;g++)
693 ch+=channels[g];
694 p->total_ch = ch;
696 p->ch=channels;
697 p->rate=rate;
699 /* generate all the text layouts we'll need */
701 char *labels[11]={"DC","2kHz","4kHz","6kHz","8kHz","10kHz","12kHz",
702 "14kHz","16kHz","18kHz",""};
703 p->lin_layout=calloc(12,sizeof(*p->lin_layout));
704 for(i=0;i<11;i++)
705 p->lin_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
708 char *labels[37]={"-180\xC2\xB0","-170\xC2\xB0","-160\xC2\xB0",
709 "-150\xC2\xB0","-140\xC2\xB0","-130\xC2\xB0",
710 "-120\xC2\xB0","-110\xC2\xB0","-100\xC2\xB0",
711 "-90\xC2\xB0","-80\xC2\xB0","-70\xC2\xB0",
712 "-60\xC2\xB0","-50\xC2\xB0","-40\xC2\xB0",
713 "-30\xC2\xB0","-20\xC2\xB0","-10\xC2\xB0",
714 "-0\xC2\xB0","+10\xC2\xB0","+20\xC2\xB0",
715 "+30\xC2\xB0","+40\xC2\xB0","+50\xC2\xB0",
716 "+60\xC2\xB0","+70\xC2\xB0","+80\xC2\xB0",
717 "+90\xC2\xB0","+100\xC2\xB0","+110\xC2\xB0",
718 "+120\xC2\xB0","+130\xC2\xB0","+140\xC2\xB0",
719 "+150\xC2\xB0","+160\xC2\xB0","+170\xC2\xB0",
720 "+180\xC2\xB0"};
721 p->phase_layout=calloc(38,sizeof(*p->phase_layout));
722 for(i=0;i<37;i++)
723 p->phase_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
726 char *labels[6]={"1Hz","10Hz","100Hz","1kHz","10kHz",""};
727 p->log_layout=calloc(7,sizeof(*p->log_layout));
728 for(i=0;i<6;i++)
729 p->log_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
732 char *labels[9]={"10M\xCE\xA9","1M\xCE\xA9","100k\xCE\xA9","10k\xCE\xA9",
733 "1k\xCE\xA9","100\xCE\xA9","10\xCE\xA9","1\xCE\xA9",".1\xCE\xA9"};
734 p->imp_layout=calloc(10,sizeof(*p->imp_layout));
735 for(i=0;i<9;i++)
736 p->imp_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
739 char *labels[10]={"31Hz","63Hz","125Hz","250Hz","500Hz","1kHz","2kHz",
740 "4kHz","8kHz","16kHz"};
741 p->iso_layout=calloc(11,sizeof(*p->iso_layout));
742 for(i=0;i<10;i++)
743 p->iso_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
746 char *labels[57]={"-140dB","-135dB","-130dB","-125dB","-120dB","-115dB",
747 "-110dB","-105dB","-100dB","-95dB","-90dB","-85dB",
748 "-80dB","-75dB","-70dB","-65dB","-60dB","-55dB","-50dB",
749 "-45dB","-40dB","-35dB","-30dB","-25dB","-20dB",
750 "-15dB","-10dB","-5dB","0dB","+5dB","+10dB","+15dB",
751 "+20dB","+25dB","+30dB","+35dB","+40dB","+45dB","+50dB",
752 "+55dB","+60dB","+65dB","+70dB","+75dB","+80dB","+85dB",
753 "+90dB","+95dB","+100dB","+105dB","+110dB","+115dB",
754 "+120dB","+125dB","+130dB","+135dB","+140dB"};
755 p->db_layout=calloc(58,sizeof(*p->db_layout));
756 for(i=0;i<57;i++)
757 p->db_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
760 p->ch_active=calloc(ch,sizeof(*p->ch_active));
761 p->ch_process=calloc(ch,sizeof(*p->ch_process));
763 plot_clear(p);
764 return ret;
767 void plot_refresh (Plot *p, int *process){
768 int i;
769 float ymax,pmax,pmin;
770 int width=GTK_WIDGET(p)->allocation.width-p->padx;
771 int height=GTK_WIDGET(p)->allocation.height-p->pady;
772 float **data;
774 if(!p->ydata)return;
776 if(process)
777 memcpy(p->ch_process,process,p->total_ch*sizeof(*process));
779 data = process_fetch(p->res, p->scale, p->mode, p->link,
780 p->ch_process,width,&ymax,&pmax,&pmin);
782 for(i=0;i<p->total_ch;i++)
783 memcpy(p->ydata[i],data[i],width*sizeof(**p->ydata));
785 /* graph limit updates are conditional depending on mode/link */
786 pmax+=5;
787 pmin-=5;
788 if(pmax<5)pmax=5;
789 if(pmin>-30)pmin=-30;
791 switch(p->link){
792 case LINK_INDEPENDENT:
793 case LINK_SUMMED:
794 case LINK_PHASE:
795 case LINK_THD:
796 case LINK_THDN:
798 float dBpp = p->depth/height;
799 ymax += dBpp*10;
801 break;
802 case LINK_IMPEDENCE_p1:
803 case LINK_IMPEDENCE_1:
804 case LINK_IMPEDENCE_10:
805 ymax *=1.8;
806 break;
809 //if(p->ymax>ymax)ymax=p->ymax;
810 //if(pmax<p->pmax)pmax=p->pmax;
811 //if(pmin>p->pmin)pmin=p->pmin;
813 /* scale regression is conditional and damped. Start the timer/run
814 the timer while any one scale measure should be dropping by more
815 than 50px. If any peaks occur above, reset timer. Once timer
816 runs out, drop 5px per frame */
817 #define PXTHRESH 25
818 #define PXDEL 10.
819 #define TIMERFRAMES 20
820 if(p->ymax>ymax){
821 float oldzero = (height-1)/p->depth*p->ymax;
822 float newzero = (height-1)/p->depth*ymax;
824 if(newzero+PXTHRESH<oldzero){
825 if(p->ymaxtimer){
826 p->ymaxtimer--;
827 }else{
828 p->ymax = (oldzero-PXDEL)*p->depth/(height-1);
830 }else{
831 p->ymaxtimer = TIMERFRAMES;
833 }else
834 p->ymaxtimer = TIMERFRAMES;
836 if(p->pmax>pmax || p->pmin<pmin){
837 float newmax = (height-1)/(p->pmax-p->pmin)*(p->pmax-pmax);
838 float newmin = (height-1)/(p->pmax-p->pmin)*(pmin-p->pmin);
840 if(newmax>PXTHRESH || newmin>PXTHRESH){
841 if(p->phtimer){
842 p->phtimer--;
843 }else{
844 if(newmax>PXTHRESH)
845 p->pmax -= PXDEL/(height-1)*(p->pmax-p->pmin);
846 if(newmin>PXTHRESH)
847 p->pmin += PXDEL/(height-1)*(p->pmax-p->pmin);
849 }else{
850 p->phtimer = TIMERFRAMES;
852 }else
853 p->phtimer = TIMERFRAMES;
855 if(ymax<p->depth-140.)ymax=p->depth-140.;
856 if(ymax>140.)ymax=140.;
857 if(pmax>180)pmax=180;
858 if(pmin<-180)pmin=-180;
859 if(ymax>p->ymax)p->ymax=ymax;
860 if(pmax>p->pmax)p->pmax=pmax;
861 if(pmin<p->pmin)p->pmin=pmin;
863 p->disp_depth = p->depth;
864 p->disp_ymax = p->ymax;
865 p->disp_pmax = p->pmax;
866 p->disp_pmin = p->pmin;
868 /* finally, align phase/response zeros on phase graphs */
869 if(p->disp_ymax>-140){
870 if(p->link == LINK_PHASE){
871 /* In a phase/response graph, 0dB/0degrees are bound and always on-screen. */
872 float mzero = (height-1)/p->disp_depth*p->disp_ymax;
873 float pzero = (height-1)/(p->disp_pmax-p->disp_pmin)*p->disp_pmax;
875 if(mzero<pzero){
876 /* straightforward; move the dB range down */
877 p->disp_ymax = pzero*p->disp_depth/(height-1);
878 }else{
879 /* a little harder as phase has a min and a max.
880 First increase the pmax to match the dB zero. */
881 p->disp_pmax = p->disp_pmin/(1-(height-1)/mzero);
882 pzero = (height-1)/(p->disp_pmax-p->disp_pmin)*p->disp_pmax;
884 /* That worked, but might have run p->max overrange */
885 if(p->disp_pmax>180.){
886 /* only way to reconcile this one is to increase the pdepth */
887 p->disp_pmax = 180.;
888 pzero = (height-1)/(p->disp_pmax-p->disp_pmin)*p->disp_pmax;
889 p->disp_depth = (height-1)/pzero*p->disp_ymax;
896 void plot_clear (Plot *p){
897 GtkWidget *widget=GTK_WIDGET(p);
898 int width=GTK_WIDGET(p)->allocation.width-p->padx;
899 int i,j;
901 if(p->ydata)
902 for(i=0;i<p->total_ch;i++)
903 for(j=0;j<width;j++)
904 p->ydata[i][j]=NAN;
905 p->ymax=p->depth-140;
906 p->pmax=0;
907 p->pmin=0;
908 draw_and_expose(widget);
911 float **plot_get (Plot *p){
912 return(p->ydata);
915 void plot_setting (Plot *p, int res, int scale, int mode, int link, int depth){
916 GtkWidget *widget=GTK_WIDGET(p);
917 p->res=res;
918 p->scale=scale;
919 p->mode=mode;
920 p->depth=depth;
921 p->link=link;
923 p->ymax=-140;
924 p->pmax=0;
925 p->pmin=0;
927 compute_metadata(widget);
928 plot_refresh(p,NULL);
929 draw_and_expose(widget);
932 void plot_draw (Plot *p){
933 GtkWidget *widget=GTK_WIDGET(p);
934 draw_and_expose(widget);
937 void plot_set_active(Plot *p, int *a, int *b){
938 GtkWidget *widget=GTK_WIDGET(p);
939 memcpy(p->ch_active,a,p->total_ch*sizeof(*a));
940 memcpy(p->ch_process,b,p->total_ch*sizeof(*b));
941 plot_refresh(p,NULL);
942 draw_and_expose(widget);