Recognizes if input is ogg or not.
[xiph/unicode.git] / spectrum / plot.c
blobd6bb12ea787223fae3dffff340e49a6ff73141c0
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 noise floor if active */
214 if(p->floor){
215 GdkColor rgb = {0,0xd000,0xd000,0xd000};
216 gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
218 for(i=0;i<width-padx;i++){
219 float val=p->floor[i];
220 int y;
222 /* No noise floor is passed back for display in the modes where it's irrelevant */
223 y= rint((height-p->pady-1)/p->disp_depth*(p->disp_ymax-val));
224 if(y<height-p->pady)
225 gdk_draw_line(p->backing,p->drawgc,padx+i,y,padx+i,height-p->pady-1);
229 /* draw the light x grid */
231 int i;
232 GdkColor rgb={0,0,0,0};
234 rgb.red=0xc000;
235 rgb.green=0xff00;
236 rgb.blue=0xff00;
237 gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
239 for(i=0;i<p->xtics;i++)
240 gdk_draw_line(p->backing,p->drawgc,p->xtic[i],0,p->xtic[i],height-p->pady);
243 /* draw the x labels */
245 PangoLayout **proper;
246 switch(p->scale){
247 case 0:
248 proper=p->log_layout;
249 break;
250 case 1:
251 proper=p->iso_layout;
252 break;
253 case 2:
254 proper=p->lin_layout;
255 break;
257 for(i=0;i<p->xgrids;i++){
258 int px,py;
259 pango_layout_get_pixel_size(proper[i],&px,&py);
261 gdk_draw_layout (p->backing,
262 widget->style->black_gc,
263 p->xgrid[i]-(px/2), height-py+2,
264 proper[i]);
268 /* draw the light y grid */
269 if(impedence){ /* impedence mode */
271 GdkColor rgb={0,0,0,0};
272 rgb.red=0xc000;
273 rgb.green=0xff00;
274 rgb.blue=0xff00;
275 gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
277 compute_imp_scale(widget);
279 for(i=0;i<p->ytics;i++)
280 gdk_draw_line(p->backing,p->drawgc,padx,p->ytic[i],width,p->ytic[i]);
282 }else{
283 float del=(height-p->pady-1)/(float)p->disp_depth,off;
284 int i,half=0;
285 int max,mul;
286 GdkColor rgb={0,0,0,0};
289 if(del>16){
290 half=1;
291 max=303;
292 mul=1;
293 off=(p->disp_ymax-ceil(p->disp_ymax))*2;
294 del*=.5;
295 }else if(del>8){
296 max=151;
297 mul=1;
298 off=p->disp_ymax-ceil(p->disp_ymax);
299 }else if(del*2>8){
300 max=76;
301 mul=2;
302 off=p->disp_ymax-ceil(p->disp_ymax*.5)*2;
303 }else{
304 max=31;
305 mul=5;
306 off=p->disp_ymax-ceil(p->disp_ymax*.2)*5;
309 rgb.red=0xc000;
310 rgb.green=0xff00;
311 rgb.blue=0xff00;
312 gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
313 gdk_gc_set_rgb_fg_color(p->dashes,&rgb);
315 for(i=0;i<max;i+=2){
316 int ymid=rint(del * (i * mul + off));
317 if(ymid>=0 && ymid<height-p->pady)
318 gdk_draw_line(p->backing,p->drawgc,padx,ymid,width,ymid);
321 for(i=1;i<max;i+=2){
322 int ymid=rint(del * (i * mul + off));
323 if(ymid>=0 && ymid<height-p->pady)
324 gdk_draw_line(p->backing,(half?p->dashes:p->drawgc),padx,ymid,width,ymid);
329 /* dark x grid */
331 int i;
332 GdkColor rgb={0,0,0,0};
334 rgb.red=0x0000;
335 rgb.green=0xc000;
336 rgb.blue=0xc000;
337 gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
339 for(i=0;i<p->xgrids;i++)
340 gdk_draw_line(p->backing,p->drawgc,p->xgrid[i],0,p->xgrid[i],height-p->pady);
343 /* dark y grid */
344 if(impedence){
345 GdkColor rgb={0,0,0,0};
346 rgb.red=0x0000;
347 rgb.green=0xc000;
348 rgb.blue=0xc000;
350 gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
352 for(i=0;i<p->ygrids;i++){
353 int px,py;
354 pango_layout_get_pixel_size(p->imp_layout[i],&px,&py);
356 gdk_draw_layout (p->backing,
357 widget->style->black_gc,
358 padx-px-2, p->ygrid[i]-py/2,
359 p->imp_layout[i]);
361 gdk_draw_line(p->backing,p->drawgc,padx,p->ygrid[i],width,p->ygrid[i]);
364 }else{
365 GdkColor rgb={0,0,0,0};
366 int label=ceil(p->disp_ymax/5+28),i;
367 float del=(height-p->pady-1)/(float)p->disp_depth,step;
368 float off=p->disp_ymax-ceil(p->disp_ymax*.2)*5;
369 step=2;
370 if(del>8)step=1;
372 for(i=0;i<32;i++){
373 if(((label-i)&1)==0 || step==1){
374 int ymid=rint(del * (i*5+off));
375 int px,py;
377 if(((label-i)&1)==0){
378 rgb.red=0x0000;
379 rgb.green=0xc000;
380 rgb.blue=0xc000;
381 }else{
382 rgb.red=0xa000;
383 rgb.green=0xe000;
384 rgb.blue=0xe000;
386 gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
388 if(label-i>=0 && label-i<57 && ymid>=0 && ymid<height-p->pady){
389 pango_layout_get_pixel_size(p->db_layout[label-i],&px,&py);
391 gdk_draw_layout (p->backing,
392 widget->style->black_gc,
393 padx-px-2, ymid-py/2,
394 p->db_layout[label-i]);
395 gdk_draw_line(p->backing,p->drawgc,padx,ymid,width,ymid);
401 /* phase? draw in phase and tics on right axis */
402 if(phase){
403 GdkColor rgb={0,0xd000,0x0000,0x0000};
404 float depth = p->disp_pmax-p->disp_pmin;
405 int label=ceil(p->disp_pmax/10+18),i;
406 float del=(height-p->pady-1)/depth,step;
407 float off=p->disp_pmax-ceil(p->disp_pmax*.1)*10;
408 step=2;
409 if(del>8)step=1;
411 gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
412 for(i=0;i<37;i++){
413 if(((label-i)&1)==0 || step==1){
414 int ymid=rint(del * (i*10+off));
415 int px,py;
417 if(label-i>=0 && label-i<37 && ymid>=0 && ymid<height-p->pady){
418 pango_layout_get_pixel_size(p->phase_layout[label-i],&px,&py);
420 gdk_draw_layout (p->backing,p->drawgc,
421 width-p->phax, ymid-py/2,
422 p->phase_layout[label-i]);
427 if(del>10){
428 for(i=0;;i++){
429 int ymid=rint(del * (i+off));
430 if(ymid>=height-p->pady)break;
431 if(ymid>=0)
432 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);
434 }else if(del>5){
435 for(i=0;;i++){
436 int ymid=rint(del * (i*2+off));
437 if(ymid>=height-p->pady)break;
438 if(ymid>=0)
439 gdk_draw_line(p->backing,p->drawgc,width-p->phax-12,ymid,width-p->phax-7,ymid);
441 } else if(del>2){
442 for(i=0;;i++){
443 int ymid=rint(del * (i*5+off));
444 if(ymid>=height-p->pady)break;
445 if(ymid>=0)
446 gdk_draw_line(p->backing,p->drawgc,width-p->phax-15,ymid,width-p->phax-5,ymid);
450 for(i=0;;i++){
451 int ymid=rint(del * (i*10+off));
452 if(ymid>=height-p->pady)break;
453 if(ymid>=0){
454 gdk_draw_line(p->backing,p->drawgc,width-p->phax-5,ymid-1,width-p->phax-2,ymid-1);
455 gdk_draw_line(p->backing,p->drawgc,width-p->phax-25,ymid,width-p->phax-2,ymid);
456 gdk_draw_line(p->backing,p->drawgc,width-p->phax-5,ymid+1,width-p->phax-2,ymid+1);
462 /* draw actual data */
463 if(p->ydata){
464 int cho=0;
465 int gi;
466 for(gi=0;gi<p->groups;gi++){
467 int ch;
468 GdkColor rgb;
470 for(ch=cho;ch<cho+p->ch[gi];ch++){
471 if(p->ch_active[ch]){
472 int prev;
473 int first=0;
474 float yprev=NAN;
476 rgb = chcolor(ch);
477 gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
479 for(i=0;i<width-padx;i++){
480 float val=p->ydata[ch][i];
481 int y;
483 if(isnan(yprev) || isnan(val)){
484 yprev = val;
485 }else{
486 yprev = val;
488 if(impedence){ /* log scale for impedence */
489 y =rint( (log10(p->disp_ymax)-log10(val))/(log10(p->disp_ymax)-log10(.1)) *
490 (height-p->pady-1));
491 }else if(phase && ch==cho+1){
492 y= rint((height-p->pady-1)/(p->disp_pmax-p->disp_pmin)*(p->disp_pmax-val));
493 }else{
494 y= rint((height-p->pady-1)/p->disp_depth*(p->disp_ymax-val));
497 if(first && (y<height-p->pady || prev<height-p->pady)){
498 int ly = y;
499 int lp = prev;
501 if(ly>=height-p->pady)ly=height-p->pady;
502 if(lp>=height-p->pady)lp=height-p->pady;
504 gdk_draw_line(p->backing,p->drawgc,padx+i-1,lp,padx+i,ly);
506 ly++;
507 lp++;
509 if(ly>=height-p->pady)ly=height-p->pady;
510 if(lp>=height-p->pady)lp=height-p->pady;
512 gdk_draw_line(p->backing,p->drawgc,padx+i-1,lp,padx+i,ly);
514 first=1;
515 prev=y;
522 cho+=p->ch[gi];
527 static void draw_and_expose(GtkWidget *widget){
528 Plot *p=PLOT(widget);
529 if(!GDK_IS_DRAWABLE(p->backing))return;
530 draw(widget);
531 if(!GTK_WIDGET_DRAWABLE(widget))return;
532 if(!GDK_IS_DRAWABLE(widget->window))return;
533 gdk_draw_drawable(widget->window,
534 widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
535 p->backing,
536 0, 0,
537 0, 0,
538 widget->allocation.width,
539 widget->allocation.height);
542 static gboolean expose( GtkWidget *widget, GdkEventExpose *event ){
543 Plot *p=PLOT(widget);
544 gdk_draw_drawable(widget->window,
545 widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
546 p->backing,
547 event->area.x, event->area.y,
548 event->area.x, event->area.y,
549 event->area.width, event->area.height);
551 return FALSE;
554 static void size_request (GtkWidget *widget,GtkRequisition *requisition){
555 Plot *p=PLOT(widget);
556 requisition->width = 800;
557 requisition->height = 440;
558 int axisy=0,axisx=0,pady=0,padx=0,phax=0,px,py,i;
560 /* find max lin layout */
562 int max=0;
563 for(i=0;p->lin_layout[i];i++){
564 pango_layout_get_pixel_size(p->lin_layout[i],&px,&py);
565 if(px>max)max=px;
566 if(py>pady)pady=py;
568 max*=i;
569 if(axisx<max)axisx=max;
571 /* find max log layout */
573 int max=0;
574 for(i=0;p->log_layout[i];i++){
575 pango_layout_get_pixel_size(p->log_layout[i],&px,&py);
576 if(px>max)max=px;
577 if(py>pady)pady=py;
579 max*=i;
580 if(axisx<max)axisx=max;
582 /* find max iso layout */
584 int max=0;
585 for(i=0;p->iso_layout[i];i++){
586 pango_layout_get_pixel_size(p->iso_layout[i],&px,&py);
587 if(px>max)max=px;
588 if(py>pady)pady=py;
590 max*=i;
591 if(axisx<max)axisx=max;
593 /* find max db layout */
595 int max=0;
596 for(i=0;p->db_layout[i];i++){
597 pango_layout_get_pixel_size(p->db_layout[i],&px,&py);
598 if(py>max)max=py;
599 if(px>padx)padx=px;
601 axisy=(max+5)*8;
602 if(axisy<max)axisx=max;
604 /* find max imped layout */
606 int max=0;
607 for(i=0;p->imp_layout[i];i++){
608 pango_layout_get_pixel_size(p->imp_layout[i],&px,&py);
609 if(py>max)max=py;
610 if(px>padx)padx=px;
612 axisy=(max+5)*8;
613 if(axisy<max)axisx=max;
615 /* find max phase layout */
617 int max=0;
618 for(i=0;p->phase_layout[i];i++){
619 pango_layout_get_pixel_size(p->phase_layout[i],&px,&py);
620 if(py>max)max=py;
621 if(px>phax)phax=px;
623 axisy=(max+5)*8;
624 if(axisy<max)axisx=max;
627 if(requisition->width<axisx+padx)requisition->width=axisx+padx;
628 if(requisition->height<axisy+pady)requisition->height=axisy+pady;
629 p->padx=padx;
630 p->pady=pady;
631 p->phax=phax;
634 static gboolean configure(GtkWidget *widget, GdkEventConfigure *event){
635 Plot *p=PLOT(widget);
637 if (p->backing)
638 g_object_unref(p->backing);
640 p->backing = gdk_pixmap_new(widget->window,
641 widget->allocation.width,
642 widget->allocation.height,
643 -1);
645 p->ydata=NULL;
646 p->configured=1;
648 compute_metadata(widget);
649 plot_refresh(p,NULL);
650 draw_and_expose(widget);
652 return TRUE;
655 static void plot_class_init (PlotClass *class){
656 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
657 parent_class = g_type_class_peek_parent (class);
659 widget_class->expose_event = expose;
660 widget_class->configure_event = configure;
661 widget_class->size_request = size_request;
664 static void plot_init (Plot *p){
665 p->mode=0;
666 p->scale=0;
667 p->depth=45.;
670 GType plot_get_type (void){
671 static GType m_type = 0;
672 if (!m_type){
673 static const GTypeInfo m_info={
674 sizeof (PlotClass),
675 NULL, /* base_init */
676 NULL, /* base_finalize */
677 (GClassInitFunc) plot_class_init,
678 NULL, /* class_finalize */
679 NULL, /* class_data */
680 sizeof (Plot),
682 (GInstanceInitFunc) plot_init,
686 m_type = g_type_register_static (GTK_TYPE_DRAWING_AREA, "Plot", &m_info, 0);
689 return m_type;
692 GtkWidget* plot_new (int size, int groups, int *channels, int *rate){
693 GtkWidget *ret= GTK_WIDGET (g_object_new (plot_get_type (), NULL));
694 Plot *p=PLOT(ret);
695 int g,i;
696 int ch=0;
698 p->groups = groups;
699 for(g=0;g<groups;g++)
700 ch+=channels[g];
701 p->total_ch = ch;
703 p->ch=channels;
704 p->rate=rate;
706 /* generate all the text layouts we'll need */
708 char *labels[11]={"DC","2kHz","4kHz","6kHz","8kHz","10kHz","12kHz",
709 "14kHz","16kHz","18kHz",""};
710 p->lin_layout=calloc(12,sizeof(*p->lin_layout));
711 for(i=0;i<11;i++)
712 p->lin_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
715 char *labels[37]={"-180\xC2\xB0","-170\xC2\xB0","-160\xC2\xB0",
716 "-150\xC2\xB0","-140\xC2\xB0","-130\xC2\xB0",
717 "-120\xC2\xB0","-110\xC2\xB0","-100\xC2\xB0",
718 "-90\xC2\xB0","-80\xC2\xB0","-70\xC2\xB0",
719 "-60\xC2\xB0","-50\xC2\xB0","-40\xC2\xB0",
720 "-30\xC2\xB0","-20\xC2\xB0","-10\xC2\xB0",
721 "-0\xC2\xB0","+10\xC2\xB0","+20\xC2\xB0",
722 "+30\xC2\xB0","+40\xC2\xB0","+50\xC2\xB0",
723 "+60\xC2\xB0","+70\xC2\xB0","+80\xC2\xB0",
724 "+90\xC2\xB0","+100\xC2\xB0","+110\xC2\xB0",
725 "+120\xC2\xB0","+130\xC2\xB0","+140\xC2\xB0",
726 "+150\xC2\xB0","+160\xC2\xB0","+170\xC2\xB0",
727 "+180\xC2\xB0"};
728 p->phase_layout=calloc(38,sizeof(*p->phase_layout));
729 for(i=0;i<37;i++)
730 p->phase_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
733 char *labels[6]={"1Hz","10Hz","100Hz","1kHz","10kHz",""};
734 p->log_layout=calloc(7,sizeof(*p->log_layout));
735 for(i=0;i<6;i++)
736 p->log_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
739 char *labels[9]={"10M\xCE\xA9","1M\xCE\xA9","100k\xCE\xA9","10k\xCE\xA9",
740 "1k\xCE\xA9","100\xCE\xA9","10\xCE\xA9","1\xCE\xA9",".1\xCE\xA9"};
741 p->imp_layout=calloc(10,sizeof(*p->imp_layout));
742 for(i=0;i<9;i++)
743 p->imp_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
746 char *labels[10]={"31Hz","63Hz","125Hz","250Hz","500Hz","1kHz","2kHz",
747 "4kHz","8kHz","16kHz"};
748 p->iso_layout=calloc(11,sizeof(*p->iso_layout));
749 for(i=0;i<10;i++)
750 p->iso_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
753 char *labels[57]={"-140dB","-135dB","-130dB","-125dB","-120dB","-115dB",
754 "-110dB","-105dB","-100dB","-95dB","-90dB","-85dB",
755 "-80dB","-75dB","-70dB","-65dB","-60dB","-55dB","-50dB",
756 "-45dB","-40dB","-35dB","-30dB","-25dB","-20dB",
757 "-15dB","-10dB","-5dB","0dB","+5dB","+10dB","+15dB",
758 "+20dB","+25dB","+30dB","+35dB","+40dB","+45dB","+50dB",
759 "+55dB","+60dB","+65dB","+70dB","+75dB","+80dB","+85dB",
760 "+90dB","+95dB","+100dB","+105dB","+110dB","+115dB",
761 "+120dB","+125dB","+130dB","+135dB","+140dB"};
762 p->db_layout=calloc(58,sizeof(*p->db_layout));
763 for(i=0;i<57;i++)
764 p->db_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
767 p->ch_active=calloc(ch,sizeof(*p->ch_active));
768 p->ch_process=calloc(ch,sizeof(*p->ch_process));
770 plot_clear(p);
771 return ret;
774 void plot_refresh (Plot *p, int *process){
775 float ymax,pmax,pmin;
776 int width=GTK_WIDGET(p)->allocation.width-p->padx;
777 int height=GTK_WIDGET(p)->allocation.height-p->pady;
778 float **data;
779 float *floor;
781 if(!p->configured)return;
783 if(process)
784 memcpy(p->ch_process,process,p->total_ch*sizeof(*process));
786 data = process_fetch(p->res, p->scale, p->mode, p->link,
787 p->ch_process,width,&ymax,&pmax,&pmin,&floor,p->noise);
789 p->ydata=data;
790 if(floor)
791 p->floor=floor;
792 else
793 p->floor=NULL;
795 /* graph limit updates are conditional depending on mode/link */
796 pmax+=5;
797 pmin-=5;
798 if(pmax<5)pmax=5;
799 if(pmin>-30)pmin=-30;
801 switch(p->link){
802 case LINK_INDEPENDENT:
803 case LINK_SUMMED:
804 case LINK_PHASE:
805 case LINK_THD:
806 case LINK_THDN:
808 float dBpp = p->depth/height;
809 ymax += dBpp*10;
811 break;
812 case LINK_IMPEDENCE_p1:
813 case LINK_IMPEDENCE_1:
814 case LINK_IMPEDENCE_10:
815 if(ymax<12)
816 ymax=12;
817 else
818 ymax *=1.8;
819 break;
822 if(p->mode == 0){
823 /* "Instantaneous' mode scale regression is conditional and
824 damped. Start the timer/run the timer while any one scale measure
825 should be dropping by more than 50px. If any peaks occur above,
826 reset timer. Once timer runs out, drop 5px per frame */
827 #define PXTHRESH 25
828 #define PXDEL 10.
829 #define TIMERFRAMES 20
830 if(p->ymax>ymax){
831 float oldzero = (height-1)/p->depth*p->ymax;
832 float newzero = (height-1)/p->depth*ymax;
834 if(newzero+PXTHRESH<oldzero){
835 if(p->ymaxtimer){
836 p->ymaxtimer--;
837 }else{
838 p->ymax = (oldzero-PXDEL)*p->depth/(height-1);
840 }else{
841 p->ymaxtimer = TIMERFRAMES;
843 }else
844 p->ymaxtimer = TIMERFRAMES;
846 if(p->pmax>pmax || p->pmin<pmin){
847 float newmax = (height-1)/(p->pmax-p->pmin)*(p->pmax-pmax);
848 float newmin = (height-1)/(p->pmax-p->pmin)*(pmin-p->pmin);
850 if(newmax>PXTHRESH || newmin>PXTHRESH){
851 if(p->phtimer){
852 p->phtimer--;
853 }else{
854 if(newmax>PXTHRESH)
855 p->pmax -= PXDEL/(height-1)*(p->pmax-p->pmin);
856 if(newmin>PXTHRESH)
857 p->pmin += PXDEL/(height-1)*(p->pmax-p->pmin);
859 }else{
860 p->phtimer = TIMERFRAMES;
862 }else
863 p->phtimer = TIMERFRAMES;
866 if(ymax<p->depth-140.)ymax=p->depth-140.;
867 if(ymax>140.)ymax=140.;
868 if(pmax>180)pmax=180;
869 if(pmin<-180)pmin=-180;
871 if(p->mode == 0){
872 if(ymax>p->ymax)p->ymax=ymax;
873 if(pmax>p->pmax)p->pmax=pmax;
874 if(pmin<p->pmin)p->pmin=pmin;
875 }else{
876 p->ymax=ymax;
877 p->pmax=pmax;
878 p->pmin=pmin;
881 p->disp_depth = p->depth;
882 p->disp_ymax = p->ymax;
883 p->disp_pmax = p->pmax;
884 p->disp_pmin = p->pmin;
886 /* finally, align phase/response zeros on phase graphs */
887 if(p->disp_ymax>-140){
888 if(p->link == LINK_PHASE){
889 /* In a phase/response graph, 0dB/0degrees are bound and always on-screen. */
890 float mzero = (height-1)/p->disp_depth*p->disp_ymax;
891 float pzero = (height-1)/(p->disp_pmax-p->disp_pmin)*p->disp_pmax;
893 if(mzero<pzero){
894 /* straightforward; move the dB range down */
895 p->disp_ymax = pzero*p->disp_depth/(height-1);
896 }else{
897 /* a little harder as phase has a min and a max.
898 First increase the pmax to match the dB zero. */
899 p->disp_pmax = p->disp_pmin/(1-(height-1)/mzero);
900 pzero = (height-1)/(p->disp_pmax-p->disp_pmin)*p->disp_pmax;
902 /* That worked, but might have run p->max overrange */
903 if(p->disp_pmax>180.){
904 /* only way to reconcile this one is to increase the pdepth */
905 p->disp_pmax = 180.;
906 pzero = (height-1)/(p->disp_pmax-p->disp_pmin)*p->disp_pmax;
907 p->disp_depth = (height-1)/pzero*p->disp_ymax;
914 void plot_clear (Plot *p){
915 GtkWidget *widget=GTK_WIDGET(p);
916 int width=GTK_WIDGET(p)->allocation.width-p->padx;
917 int i,j;
919 if(p->ydata)
920 for(i=0;i<p->total_ch;i++)
921 for(j=0;j<width;j++)
922 p->ydata[i][j]=NAN;
923 p->ymax=p->depth-140;
924 p->pmax=0;
925 p->pmin=0;
926 draw_and_expose(widget);
929 float **plot_get (Plot *p){
930 return(p->ydata);
933 void plot_setting (Plot *p, int res, int scale, int mode, int link, int depth, int noise){
934 GtkWidget *widget=GTK_WIDGET(p);
935 p->res=res;
936 p->scale=scale;
937 p->mode=mode;
938 p->depth=depth;
939 p->link=link;
940 p->noise=noise;
942 p->ymax=-140;
943 p->pmax=0;
944 p->pmin=0;
946 compute_metadata(widget);
947 plot_refresh(p,NULL);
948 draw_and_expose(widget);
951 void plot_draw (Plot *p){
952 GtkWidget *widget=GTK_WIDGET(p);
953 draw_and_expose(widget);
956 void plot_set_active(Plot *p, int *a, int *b){
957 GtkWidget *widget=GTK_WIDGET(p);
958 memcpy(p->ch_active,a,p->total_ch*sizeof(*a));
959 memcpy(p->ch_process,b,p->total_ch*sizeof(*b));
960 plot_refresh(p,NULL);
961 draw_and_expose(widget);