[xiph-commits] r18342 - trunk/spectrum

xiphmont at svn.xiph.org xiphmont at svn.xiph.org
Thu May 31 11:54:57 PDT 2012


Author: xiphmont
Date: 2012-05-31 11:54:57 -0700 (Thu, 31 May 2012)
New Revision: 18342

Modified:
   trunk/spectrum/spec_plot.c
   trunk/spectrum/spec_plot.h
Log:
Reimplement autoscale with critically damped IIR filters.



Modified: trunk/spectrum/spec_plot.c
===================================================================
--- trunk/spectrum/spec_plot.c	2012-05-30 22:21:31 UTC (rev 18341)
+++ trunk/spectrum/spec_plot.c	2012-05-31 18:54:57 UTC (rev 18342)
@@ -68,9 +68,9 @@
 		     .5,.4,.3,.2};
 
   for(i=0;i<9;i++)
-    p->ygrid[i]=rint( (log10(p->disp_ymax)-log10(lfreqs[i]))/(log10(p->disp_ymax)-log10(.1)) * (height-1));
+    p->ygrid[i]=rint( (log10(p->ymax)-log10(lfreqs[i]))/(log10(p->ymax)-log10(.1)) * (height-1));
   for(i=0;i<64;i++)
-    p->ytic[i]=rint( (log10(p->disp_ymax)-log10(tfreqs[i]))/(log10(p->disp_ymax)-log10(.1)) * (height-1));
+    p->ytic[i]=rint( (log10(p->ymax)-log10(tfreqs[i]))/(log10(p->ymax)-log10(.1)) * (height-1));
   p->ygrids=9;
   p->ytics=64;
 
@@ -127,7 +127,7 @@
   case 1: /* ISO log */
     {
       for(i=0;i<12;i++){
-        if(iso_lfreqs[i]<(nyq-.1)){
+       if(iso_lfreqs[i]<(nyq-.1)){
           p->xgrids=i+1;
           p->xlgrids=i+1;
         }
@@ -300,7 +300,7 @@
       int y;
 
       /* No noise floor is passed back for display in the modes where it's irrelevant */
-      y= rint((height-p->pady-1)/p->disp_depth*(p->disp_ymax-val));
+      y= rint((height-p->pady-1)/p->depth*(p->ymax-val));
       if(y<height-p->pady)
 	gdk_draw_line(p->backing,p->drawgc,padx+i,y,padx+i,height-p->pady-1);
     }
@@ -386,8 +386,8 @@
   }else{
     GdkColor rgb={0,0,0,0};
     float emheight = (height-p->pady)/p->pady;
-    float emperdB = emheight/p->disp_depth;
-    float pxperdB = (height-p->pady)/p->disp_depth;
+    float emperdB = emheight/p->depth;
+    float pxperdB = (height-p->pady)/p->depth;
 
     /* we want no more than <n> major lines per graph */
     int maxmajorper = 15;
@@ -411,7 +411,7 @@
       /* minimum em seperation? */
       if(emperdB>majorsep*majordeltest[i] &&
          /* Not over the number of lines limit? */
-         p->disp_depth*majordeltest[i]<maxmajorper){
+         p->depth*majordeltest[i]<maxmajorper){
         majordel=majordellist[i];
         break;
       }
@@ -455,11 +455,11 @@
     gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
     gdk_gc_set_rgb_fg_color(p->dashes,&rgb);
 
-    float ymin = (p->disp_ymax - p->disp_depth)*1000;
-    int yval = rint((p->disp_ymax*1000/subminordel)+1)*subminordel;
+    float ymin = (p->ymax - p->depth)*1000;
+    int yval = rint((p->ymax*1000/subminordel)+1)*subminordel;
 
     while(1){
-      float ydel = (yval - ymin)/(p->disp_depth*1000);
+      float ydel = (yval - ymin)/(p->depth*1000);
       int ymid = rint(height-p->pady-1 - (height-p->pady) * ydel);
 
       if(ymid>=height-p->pady)break;
@@ -481,8 +481,8 @@
     rgb.blue=0xc000;
     gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
 
-    ymin = (p->disp_ymax - p->disp_depth)*1000;
-    yval = rint((p->disp_ymax*1000/majordel)+1)*majordel;
+    ymin = (p->ymax - p->depth)*1000;
+    yval = rint((p->ymax*1000/majordel)+1)*majordel;
 
     {
       int px,py,pxdB,pxN,pxMAX;
@@ -492,7 +492,7 @@
       pxMAX+=pxdB;
 
       while(1){
-        float ydel = (yval - ymin)/(p->disp_depth*1000);
+        float ydel = (yval - ymin)/(p->depth*1000);
         int ymid = rint(height-p->pady-1 - (height-p->pady) * ydel);
 
         if(ymid>=height-p->pady)break;
@@ -607,26 +607,26 @@
               //if(impedence){ /* log scale for impedence */
               if(0){
 
-                ymin = rint( (log10(p->disp_ymax)-log10(valmin))/
-                             (log10(p->disp_ymax)-log10(.1)) *
+                ymin = rint( (log10(p->ymax)-log10(valmin))/
+                             (log10(p->ymax)-log10(.1)) *
                              (height-p->pady-1));
-                ymax = rint( (log10(p->disp_ymax)-log10(valmax))/
-                             (log10(p->disp_ymax)-log10(.1)) *
+                ymax = rint( (log10(p->ymax)-log10(valmax))/
+                             (log10(p->ymax)-log10(.1)) *
                              (height-p->pady-1));
 
               }else if(phase && ch==cho+1){
 
                 ymin = rint((height-p->pady-1)/
-                            (p->disp_pmax-p->disp_pmin)*
-                            (p->disp_pmax-valmin));
+                            (p->pmax-p->pmin)*
+                            (p->pmax-valmin));
                 ymax = rint((height-p->pady-1)/
-                            (p->disp_pmax-p->disp_pmin)*
-                            (p->disp_pmax-valmax));
+                            (p->pmax-p->pmin)*
+                            (p->pmax-valmax));
 
               }else{
 
-                ymin = rint((height-p->pady-1)/p->disp_depth*(p->disp_ymax-valmin));
-                ymax = rint((height-p->pady-1)/p->disp_depth*(p->disp_ymax-valmax));
+                ymin = rint((height-p->pady-1)/p->depth*(p->ymax-valmin));
+                ymax = rint((height-p->pady-1)/p->depth*(p->ymax-valmax));
 
               }
 
@@ -641,24 +641,23 @@
 
   /* phase?  draw in phase and tics on right axis */
   if(phase){
-    float depth = p->disp_pmax-p->disp_pmin;
-    int label=ceil(p->disp_pmax/10+18),i;
+    float depth = p->pmax-p->pmin;
+    int label=ceil(p->pmax/10+18),i;
     float del=(height-p->pady-1)/depth,step;
-    float off=p->disp_pmax-ceil(p->disp_pmax*.1)*10;
+    float off=p->pmax-ceil(p->pmax*.1)*10;
     step=2;
     if(del>8)step=1;
 
-    for(i=0;i<38;i++){
-      if(((label-i)&1)==0 || step==1){
-	int ymid=rint(del * (i*10+off));
+    for(i=0;;i++){
+      int ymid=rint(del * (i*10+off));
+      int pv = rint((p->pmax - ymid/(float)(height-p->pady) * (p->pmax - p->pmin))/10);
+      if(ymid>=height-p->pady)break;
+      if(ymid>=0 && pv>=-18 && pv<=18 && (pv&1)==0){
 	int px,py;
-
-	if(label-i>=0 && label-i<37 && ymid>=p->pady/2 && ymid<height-p->pady/2){
-          pango_layout_get_pixel_size(p->phase_layout[label-i],&px,&py);
-	  gdk_draw_layout (p->backing,p->phasegc,
-			   width-p->phax+2, ymid-py/2,
-			   p->phase_layout[label-i]);
-	}
+        pango_layout_get_pixel_size(p->phase_layout[pv+18],&px,&py);
+        gdk_draw_layout (p->backing,p->phasegc,
+                         width-p->phax+2, ymid-py/2,
+                         p->phase_layout[pv+18]);
       }
     }
 
@@ -846,6 +845,174 @@
   p->phax=phax+2;
 }
 
+
+static void filter_reset(pole2 *p, double val){
+  p->x[0]=p->x[1]=val;
+  p->y[0]=p->y[1]=val;
+}
+
+static void filter_make_critical(double w, pole2 *f){
+  double w0 = tan(M_PI*w*pow(pow(2,.5)-1,-.5));
+  f->a  = w0*w0/(1+(2*w0)+w0*w0);
+  f->b1 = 2*f->a*(1/(w0*w0)-1);
+  f->b2 = 1-(4*f->a+f->b1);
+  filter_reset(f,0);
+}
+
+static double filter_filter(double x, pole2 *p){
+  double y =
+    p->a*x + 2*p->a*p->x[0] + p->a*p->x[1] +
+    p->b1*p->y[0] + p->b2*p->y[1];
+  p->y[1] = p->y[0]; p->y[0] = y;
+  p->x[1] = p->x[0]; p->x[0] = x;
+  return y;
+}
+
+#define THRESH .25
+#define TIMERFRAMES 20
+
+void plot_rescale (Plot *p, int request_reset){
+  float ymax,pmax,pmin;
+  int phase = phase_active_p(p);
+  int width=GTK_WIDGET(p)->allocation.width-p->padx-(phase ? p->phax : 0);
+  int height=GTK_WIDGET(p)->allocation.height-p->pady;
+  float **data;
+
+  if(!p->configured)return;
+
+  data = process_fetch(p->scale, p->mode, p->link,
+		       p->ch_process,width,&ymax,&pmax,&pmin);
+
+  p->ydata=data;
+
+  if(!p->autoscale) return;
+
+  /* graph limit updates are conditional depending on mode/link */
+  switch(p->link){
+  case LINK_INDEPENDENT:
+  case LINK_SUMMED:
+  case LINK_PHASE:
+    {
+      float dBpp = p->depth/height;
+      ymax += dBpp*25;
+    }
+    break;
+  }
+  if(ymax<p->depth-p->ymax_limit)ymax=p->depth-p->ymax_limit;
+  if(ymax>p->ymax_limit)ymax=p->ymax_limit;
+
+  pmax+=10;
+  pmin-=10;
+  if(pmax<5)pmax=5;
+  if(pmax>190)pmax=190;
+  if(pmin>-20)pmin=-20;
+  if(pmin<-190)pmin=-190;
+
+
+  /* phase/response zeros align on phase graphs; verify targets
+     against phase constraints */
+  if(phase){
+    float pzero,mzero = height/p->depth*ymax;
+
+    /* move mag zero back onscreen if it's off */
+    if(mzero < height*THRESH){
+      ymax = (p->depth*height*THRESH)/height;
+    }
+    if(mzero > height*(1-THRESH)){
+      ymax = (p->depth*height*(1-THRESH))/height;
+    }
+
+    mzero = height/p->depth*ymax;
+    pzero = height/(pmax-pmin)*pmax;
+
+    if(mzero<pzero){
+      /* straightforward; move the dB range down */
+      ymax = pzero*p->depth/height;
+    }else{
+      /* a little harder as phase has a min and a max.
+         First increase the pmax to match the dB zero. */
+
+      pmax = pmin/(1-height/mzero);
+      pzero = height/(pmax-pmin)*pmax;
+
+      /* That worked, but might have run p->max overrange */
+      if(pmax>190.){
+        /* reconcile by allowing mag to overrange */
+        pmax = 190.;
+        pzero = height/(pmax-pmin)*pmax;
+        ymax = p->depth*pzero/height;
+        p->ymaxtimer=0;
+      }
+    }
+  }
+
+  if(request_reset){
+    p->ymax=p->ymax_target=ymax;
+    filter_reset(&p->ymax_damp,p->ymax);
+    p->ymaxtimer=TIMERFRAMES;
+
+    if(phase){
+      p->pmax=p->pmax_target=pmax;
+      p->pmin=p->pmin_target=pmin;
+      filter_reset(&p->pmax_damp,p->pmax);
+      filter_reset(&p->pmin_damp,p->pmin);
+      p->pmaxtimer=TIMERFRAMES;
+      p->pmintimer=TIMERFRAMES;
+    }
+
+  }else{
+    /* conditionally set new damped ymax target */
+    if(p->ymaxtimer>0 && ymax < p->ymax*(1-THRESH))
+      p->ymaxtimer--;
+
+    if(ymax > p->ymax_target){
+      p->ymax_target=ymax;
+      p->ymaxtimer=TIMERFRAMES;
+    }
+
+    if(p->ymaxtimer<=0)
+      p->ymax_target=ymax;
+
+    /* update ymax through scale damping filter */
+    p->ymax = filter_filter(p->ymax_target,&p->ymax_damp);
+
+    /* apply same hyteresis and update to phase */
+    if(phase){
+
+      if(p->pmaxtimer>0 && pmax < p->pmax*(1-THRESH))
+        p->pmaxtimer--;
+      if(pmax > p->pmax_target){
+        p->pmax_target=pmax;
+        p->pmaxtimer=TIMERFRAMES;
+      }
+      if(p->pmaxtimer<=0)
+        p->pmax_target=pmax;
+      p->pmax = filter_filter(p->pmax_target,&p->pmax_damp);
+
+      if(p->pmintimer>0 && pmin > p->pmin*(1-THRESH))
+        p->pmintimer--;
+      if(pmin < p->pmin_target){
+        p->pmin_target=pmin;
+        p->pmintimer=TIMERFRAMES;
+      }
+      if(p->pmintimer<=0)
+        p->pmin_target=pmin;
+      p->pmin = filter_filter(p->pmin_target,&p->pmin_damp);
+    }
+  }
+
+  if(phase){
+    /* when phase is active, the p->ymax in use is dictated by
+       p->pmax/p->pmin, which in turn already took the desired ymax
+       into consideration above */
+    p->ymax = p->depth*p->pmax/(p->pmax-p->pmin);
+  }
+
+  fprintf(stderr,"ymax=(%f|%f->%f) pmax=(%f|%f) pmin=(%f|%f) \n",
+          ymax, p->ymax, p->ymax_target, pmax,p->pmax,pmin,p->pmin);
+
+}
+
 static gboolean configure(GtkWidget *widget, GdkEventConfigure *event){
   Plot *p=PLOT(widget);
 
@@ -860,7 +1027,7 @@
   p->configured=1;
 
   compute_metadata(widget);
-  plot_refresh(p,NULL);
+  plot_rescale(p,0);
   draw_and_expose(widget);
 
   return TRUE;
@@ -1033,164 +1200,22 @@
 
   p->autoscale=1;
 
+  filter_make_critical(.04,&p->ymax_damp);
+  filter_make_critical(.04,&p->pmax_damp);
+  filter_make_critical(.04,&p->pmin_damp);
+
   plot_clear(p);
   return ret;
 }
 
 void plot_refresh (Plot *p, int *process){
-  float ymax,pmax,pmin;
-  int phase = phase_active_p(p);
-  int width=GTK_WIDGET(p)->allocation.width-p->padx-(phase ? p->phax : 0);
-  int height=GTK_WIDGET(p)->allocation.height-p->pady;
-  float **data;
 
-#define THRESH .25
-#define PXDEL 10.
-#define TIMERFRAMES 20
-
   if(!p->configured)return;
 
   if(process)
     memcpy(p->ch_process,process,p->total_ch*sizeof(*process));
 
-  data = process_fetch(p->scale, p->mode, p->link,
-		       p->ch_process,width,&ymax,&pmax,&pmin);
-
-  p->ydata=data;
-
-  /* graph limit updates are conditional depending on mode/link */
-  pmax+=5;
-  pmin-=5;
-  if(pmax<5)pmax=5;
-  if(pmin>-30)pmin=-30;
-
-  switch(p->link){
-  case LINK_INDEPENDENT:
-  case LINK_SUMMED:
-  case LINK_PHASE:
-    {
-      float dBpp = p->depth/height;
-      ymax += dBpp*10;
-    }
-    break;
-#if 0
-  case LINK_IMPEDENCE_p1:
-  case LINK_IMPEDENCE_1:
-  case LINK_IMPEDENCE_10:
-    if(ymax<12)
-      ymax=12;
-    else
-      ymax*=1.8;
-    break;
-#endif
-  }
-
-  if(p->mode == 0){
-    /* "Instantaneous' mode scale regression is conditional and
-       damped. Start the timer/run the timer while any one scale
-       measure should be dropping by more than 25% of the current
-       depth. If any peaks occur above, reset timer.  Once timer runs
-       out, drop PXEDL px per frame */
-    /* todo:  might be nice to use a bessel function to track the scale
-       shifts */
-    if(p->ymax>ymax){
-      float oldzero = height/p->depth*p->ymax;
-      float newzero = height/p->depth*ymax;
-
-      if(oldzero-newzero > height*THRESH){
-        if(p->ymaxtimer){
-          p->ymaxtimer--;
-        }else{
-            ymax = (oldzero-PXDEL)*p->depth/(height-1);
-        }
-      }else{
-        p->ymaxtimer = TIMERFRAMES;
-      }
-    }else
-      p->ymaxtimer = TIMERFRAMES;
-
-    if(p->pmax>pmax || p->pmin<pmin){
-      float newmax = height/(p->pmax-p->pmin)*(p->pmax-pmax);
-      float newmin = height/(p->pmax-p->pmin)*(pmin-p->pmin);
-
-      if(newmax>height*THRESH || newmin>height*THRESH){
-	if(p->phtimer){
-	  p->phtimer--;
-	}else{
-	  if(newmax>height*THRESH)
-	    p->pmax -= PXDEL/(height-1)*(p->pmax-p->pmin);
-	  if(newmin>height*THRESH)
-	    p->pmin += PXDEL/(height-1)*(p->pmax-p->pmin);
-	}
-      }else{
-	p->phtimer = TIMERFRAMES;
-      }
-    }else
-      p->phtimer = TIMERFRAMES;
-  }
-
-  if(ymax<p->depth-p->ymax_limit)ymax=p->depth-p->ymax_limit;
-  if(ymax>p->ymax_limit)ymax=p->ymax_limit;
-  if(pmax>180)pmax=180;
-  if(pmin<-180)pmin=-180;
-  pmax+=10;
-  pmin-=10;
-
-  p->disp_depth = p->depth;
-
-  if(p->autoscale){
-    if(p->mode == 0){
-      if(ymax>p->ymax)p->ymax=ymax;
-      if(pmax>p->pmax)p->pmax=pmax;
-      if(pmin<p->pmin)p->pmin=pmin;
-    }else{
-      p->ymax=ymax;
-      p->pmax=pmax;
-      p->pmin=pmin;
-    }
-
-    p->disp_ymax = p->ymax;
-    p->disp_pmax = p->pmax;
-    p->disp_pmin = p->pmin;
-  }
-
-  /* finally, align phase/response zeros on phase graphs */
-  if(p->disp_ymax>-p->ymax_limit){
-    if(phase){
-      /* In a phase/response graph, 0dB/0degrees are bound and always on-screen. */
-      float pzero,mzero = (height-1)/p->disp_depth*p->disp_ymax;
-
-      /* move mag zero back onscreen if it's off */
-      if(mzero < height*THRESH){
-        p->disp_ymax = (p->disp_depth*height*THRESH)/(height-1);
-      }
-      if(mzero > height*(1-THRESH)){
-        p->disp_ymax = (p->disp_depth*height*(1-THRESH))/(height-1);
-      }
-
-      mzero = (height-1)/p->disp_depth*p->disp_ymax;
-      pzero = (height-1)/(p->disp_pmax-p->disp_pmin)*p->disp_pmax;
-
-      if(mzero<pzero){
-	/* straightforward; move the dB range down */
-	p->disp_ymax = pzero*p->disp_depth/(height-1);
-      }else{
-	/* a little harder as phase has a min and a max.
-	   First increase the pmax to match the dB zero. */
-
-	p->disp_pmax = p->disp_pmin/(1-(height-1)/mzero);
-	pzero = (height-1)/(p->disp_pmax-p->disp_pmin)*p->disp_pmax;
-
-	/* That worked, but might have run p->max overrange */
-	if(p->disp_pmax>190.){
-	  /* reconcile by allowing mag to overrange */
-	  p->disp_pmax = 190.;
-	  pzero = (height-1)/(p->disp_pmax-p->disp_pmin)*p->disp_pmax;
-          p->disp_ymax = p->disp_depth*pzero/(height-1);
-	}
-      }
-    }
-  }
+  plot_rescale(p, 0);
 }
 
 void plot_clear (Plot *p){
@@ -1203,9 +1228,14 @@
     for(i=0;i<p->total_ch;i++)
       for(j=0;j<width*2;j++)
 	p->ydata[i][j]=-300;
-  p->ymax=p->depth-p->ymax_limit;
-  p->pmax=0;
-  p->pmin=0;
+
+  p->ymax_target=p->ymax;
+  p->pmax_target=p->pmax;
+  p->pmin_target=p->pmin;
+  filter_reset(&p->ymax_damp,p->ymax);
+  filter_reset(&p->pmax_damp,p->pmax);
+  filter_reset(&p->pmin_damp,p->pmin);
+
   draw_and_expose(widget);
 }
 
@@ -1215,23 +1245,32 @@
 
 void plot_setting (Plot *p, int scale, int mode, int link, int depth, int noise){
   GtkWidget *widget=GTK_WIDGET(p);
+  int phase = phase_active_p(p);
+  int request_scale_reset=0;
+
   p->scale=scale;
-  p->mode=mode;
-  p->depth=depth;
-  p->link=link;
   p->noise=noise;
 
-  if(depth>140)
-    p->ymax_limit=depth;
-  else
-    p->ymax_limit=140;
+  if(p->depth != depth ||
+     p->mode != mode ||
+     p->link!=link){
+    if(depth>140)
+      p->ymax_limit=depth;
+    else
+      p->ymax_limit=140;
+    request_scale_reset=1;
+  }
 
-  p->ymax=-p->ymax_limit;
-  p->pmax=0;
-  p->pmin=0;
+  compute_metadata(widget);
 
-  compute_metadata(widget);
-  plot_refresh(p,NULL);
+  if(!phase && phase_active_p(p))
+    request_scale_reset=1;
+
+  p->depth=depth;
+  p->mode=mode;
+  p->link=link;
+
+  plot_rescale(p,request_scale_reset);
   draw_and_expose(widget);
 }
 
@@ -1244,14 +1283,14 @@
   GtkWidget *widget=GTK_WIDGET(p);
   memcpy(p->ch_active,a,p->total_ch*sizeof(*a));
   memcpy(p->ch_process,b,p->total_ch*sizeof(*b));
-  plot_refresh(p,NULL);
+  plot_rescale(p,1);
   draw_and_expose(widget);
 }
 
 void plot_set_autoscale(Plot *p, int a){
   GtkWidget *widget=GTK_WIDGET(p);
   p->autoscale=a;
-  plot_refresh(p,NULL);
+  plot_rescale(p,a);
   draw_and_expose(widget);
 }
 

Modified: trunk/spectrum/spec_plot.h
===================================================================
--- trunk/spectrum/spec_plot.h	2012-05-30 22:21:31 UTC (rev 18341)
+++ trunk/spectrum/spec_plot.h	2012-05-31 18:54:57 UTC (rev 18342)
@@ -39,6 +39,14 @@
 typedef struct _Plot       Plot;
 typedef struct _PlotClass  PlotClass;
 
+typedef struct {
+  double a;
+  double b1;
+  double b2;
+  double x[2];
+  double y[2];
+} pole2;
+
 struct _Plot{
 
   GtkDrawingArea canvas;  
@@ -96,19 +104,23 @@
   float pmax;
   float pmin;
 
-  float disp_depth;
-  float disp_ymax;
-  float disp_pmax;
-  float disp_pmin;
+  pole2 ymax_damp;
+  pole2 pmax_damp;
+  pole2 pmin_damp;
 
+  float ymax_target;
+  float pmax_target;
+  float pmin_target;
+  int ymaxtimer;
+  int pmaxtimer;
+  int pmintimer;
+
   float ymax_limit;
 
   float padx;
   float phax;
   float pady;
 
-  int ymaxtimer;
-  int phtimer;
 
   int bold;
   int autoscale;



More information about the commits mailing list