Julius 4.2
julius/visual.c
説明を見る。
00001 
00027 /*
00028  * Copyright (c) 2003-2005 Shikano Lab., Nara Institute of Science and Technology
00029  * Copyright (c) 2005-2011 Julius project team, Nagoya Institute of Technology
00030  * All rights reserved
00031  */
00032 
00033 #include <libmain.h>
00034 #include "app.h"
00035 
00036 #ifdef VISUALIZE
00037 
00038 #include <gtk/gtk.h>
00039 
00040 /* Window constant properties */
00041 #define WINTITLE "Julius word trellis viewer" ///< Window title
00042 #define DEFAULT_WINDOW_WIDTH 800 ///< Default window width
00043 #define DEFAULT_WINDOW_HEIGHT 600 ///< Default window height
00044 #define CANVAS_MARGIN 16 ///< Margin of drawable
00045 #define WAVE_HEIGHT 48 ///< Height of wave drawing canvas
00046 #define WAVE_MARGIN 6 ///< Height margin of wave drawing canvas
00047 #define RESULT_HEIGHT 0 ///< Offset of text output for each node
00048 #define FONTSET "-*-fixed-medium-r-normal--10-*-*-*-*-*-*-*" ///< Fontset
00049 
00050 /* global valuables for window handling */
00051 static GdkFont *fontset;        
00052 static GdkPixmap *pixmap = NULL; 
00053 static gint canvas_width;       
00054 static gint canvas_height;      
00055 
00056 
00057 /**********************************************************************/
00058 /* view configuration switches */
00059 /**********************************************************************/
00060 static boolean sw_wid_axis = TRUE; 
00061 static boolean sw_score_beam = FALSE; 
00062 static boolean sw_text = TRUE;  
00063 static boolean sw_line = TRUE;  
00064 static boolean sw_level_thres = FALSE; 
00065 static boolean sw_hypo = FALSE;         
00066 
00067 /**********************************************************************/
00068 /* data to plot (1st pass / 2nd pass) */
00069 /**********************************************************************/
00070 static Recog *re;               
00071 /* data to plot on 1st pass */
00072 static BACKTRELLIS *btlocal = NULL; 
00073 /* data to plot on 2nd pass */
00074 static POPNODE *popped = NULL;  
00075 static int pnum;                
00076 static POPNODE *lastpop = NULL; 
00077 
00078 
00079 /**********************************************************************/
00080 /* GTK color allocation */
00081 /**********************************************************************/
00082 
00083 #define NCOLS 15
00084 static GdkColor cols[NCOLS] = {
00085   {0, 63000, 63000, 63000},     /* background */
00086   {0,     0,     0, 40000},     /* waveform */
00087   {0, 50000, 20000,     0},     /* waveform level threshold line */
00088   {0,     0,     0, 65535},     /* begin node of word arc */
00089   {0, 60000, 60000,     0},     /* end node of word arc */
00090   {0, 24000, 32000, 24000},     /* line (context word / word graph) */
00091   {0, 10000, 10000, 40000},     /* text (context word / word graph) */
00092   {0, 50000, 54000, 50000},     /* line (word indexed trellis = all survived words) */
00093   {0, 50000, 30000,     0},     /* line (best path) */
00094   {0, 55535,     0,     0},     /* end node (best path) */
00095   {0, 50000, 30000,     0},     /* text (best path) */
00096   {0, 12000, 20000, 12000},     /* line and text (2nd pass) */
00097   {0, 50000, 50000, 12000},     /* line and text (2nd pass popped) */
00098   {0, 50000,     0,     0},     /* 2nd pass best */
00099   {0,     0,     0,     0}      /* shadow for text decoration */
00100 };
00101 typedef enum {C_BG, C_WAVEFORM, C_LEVELTHRES, C_BGN, C_END, C_LINE, C_TEXT, C_LINE_FAINT, C_LINE_BEST, C_END_BEST, C_TEXT_BEST, C_PASS2_NEXT, C_PASS2, C_PASS2_BEST, C_SHADOW} UserColors;
00102 
00103 static GdkGC *dgc = NULL;               
00104 
00113 static void
00114 color_init()
00115 {
00116   GdkColormap *defcolmap;
00117   gboolean success[NCOLS];
00118   
00119   defcolmap = gdk_colormap_get_system();
00120   if (gdk_colormap_alloc_colors(defcolmap, cols, NCOLS, FALSE, TRUE, success) > 0) {
00121     fprintf(stderr, "Warning: some colors are not allocated\n");
00122   }
00123 
00124 }
00125 
00126 
00127 /**********************************************************************/
00128 /* graph scaling */
00129 /**********************************************************************/
00130 
00131 static LOGPROB *ftop = NULL;    
00132 static LOGPROB *fbottom = NULL; 
00133 static LOGPROB lowest;          
00134 static LOGPROB maxrange;        
00135 static LOGPROB maxrange2;       
00136 
00149 static void
00150 get_max_frame_score(BACKTRELLIS *bt)
00151 {
00152   int t, i;
00153   TRELLIS_ATOM *tre;
00154   LOGPROB x,y;
00155 
00156   /* allocate */
00157   if (ftop != NULL) free(ftop);
00158   ftop = mymalloc(sizeof(LOGPROB) * bt->framelen);
00159   if (fbottom != NULL) free(fbottom);
00160   fbottom = mymalloc(sizeof(LOGPROB) * bt->framelen);
00161 
00162   /* get maxrange, ftop[], fbottom[] */
00163   maxrange = 0.0;
00164   for (t=0;t<bt->framelen;t++) {
00165     x = LOG_ZERO;
00166     y = 0.0;
00167     for (i=0;i<bt->num[t];i++) {
00168       tre = bt->rw[t][i];
00169       if (x < tre->backscore) x = tre->backscore;
00170       if (y > tre->backscore) y = tre->backscore;
00171     }
00172     ftop[t] = x;
00173     fbottom[t] = y;
00174     if (maxrange < x - y) maxrange = x - y;
00175   }
00176 
00177   /* get the lowest score and range around y=(lowest/framelen)x */
00178   lowest = 0.0;
00179   for (t=0;t<bt->framelen;t++) {
00180     if (lowest > fbottom[t]) lowest = fbottom[t];
00181   }
00182   maxrange2 = 0.0;
00183   for (t=0;t<bt->framelen;t++) {
00184     x = lowest * (float)t / (float)bt->framelen;
00185     if (ftop[t] == LOG_ZERO) continue;
00186     if (maxrange2 < abs(ftop[t] - x)) maxrange2 = abs(ftop[t] - x);
00187     if (maxrange2 < abs(fbottom[t] - x)) maxrange2 = abs(fbottom[t] - x);
00188   }
00189 }
00190 
00207 static gint
00208 scale_x(int t)
00209 {
00210   return(t * (canvas_width - CANVAS_MARGIN * 2) / btlocal->framelen + CANVAS_MARGIN);
00211 }
00212 
00231 static gint
00232 scale_y(LOGPROB s, int t)
00233 {
00234   gint y;
00235   LOGPROB top, bottom;
00236   gint yoffset, height;
00237   
00238   if (sw_score_beam) {
00239     /* beam threshold-based: upper is the maximum score on the frame */
00240     top = ftop[t];
00241     if (top == LOG_ZERO) {      /* no token found on the time */
00242       bottom = top;
00243     } else {
00244       bottom = ftop[t] - maxrange;
00245     }
00246   } else {
00247     /* total score based: show around (lowest/framelen) x time */
00248     top = lowest * (float)t / (float)btlocal->framelen + maxrange2;
00249     bottom = lowest * (float)t / (float)btlocal->framelen - maxrange2;
00250   }
00251 
00252   yoffset = CANVAS_MARGIN + RESULT_HEIGHT + (re->speechlen != 0 ? (WAVE_MARGIN + WAVE_HEIGHT): 0);
00253   height = canvas_height - yoffset - CANVAS_MARGIN;
00254   if (top <= bottom) {  /* single or no token on the time */
00255     y = yoffset;
00256   } else {
00257     y = (top - s) * height / (top - bottom) + yoffset;
00258   }
00259   return(y);
00260 }
00261 
00278 static gint
00279 scale_y_wid(WORD_ID wid)
00280 {
00281   gint y;
00282   gint yoffset, height;
00283   
00284   yoffset = CANVAS_MARGIN + RESULT_HEIGHT + (re->speechlen != 0 ? (WAVE_MARGIN + WAVE_HEIGHT) : 0);
00285   height = canvas_height - yoffset - CANVAS_MARGIN;
00286   if (wid == WORD_INVALID) {
00287     y = yoffset;
00288   } else {
00289     y = wid * height / re->model->winfo->num + yoffset;
00290   }
00291   return(y);
00292 }
00293 
00294 
00295 /**********************************************************************/
00296 /* Draw wave data */
00297 /**********************************************************************/
00298 static SP16 max_level;          
00299 
00316 static gint
00317 scale_x_wave(int t)
00318 {
00319   return(t * (canvas_width - CANVAS_MARGIN * 2) / re->speechlen + CANVAS_MARGIN);
00320 }
00321 
00338 static gint
00339 scale_y_wave(SP16 x)
00340 {
00341   return(WAVE_HEIGHT / 2 + WAVE_MARGIN - (x * WAVE_HEIGHT / (max_level * 2)));
00342 }
00343 
00354 static void
00355 get_max_waveform_level()
00356 {
00357   int t;
00358   SP16 maxl;
00359   
00360   if (re->speechlen == 0) return;       /* no waveform data (MFCC) */
00361   
00362   maxl = 0;
00363   for(t=0;t<re->speechlen;t++) {
00364     if (maxl < abs(re->speech[t])) {
00365       maxl = abs(re->speech[t]);
00366     }
00367   }
00368 
00369   max_level = maxl;
00370   if (max_level < 3000) max_level = 3000;
00371 }
00372 
00385 static void
00386 draw_waveform(GtkWidget *widget)
00387 {
00388   int t;
00389   gint text_width;
00390   static char buf[20];
00391 
00392   if (re->speechlen == 0) return;       /* no waveform data (MFCC) */
00393 
00394   /* first time, make gc for drawing */
00395   if (dgc == NULL) {
00396     dgc = gdk_gc_new(widget->window);
00397     gdk_gc_copy(dgc, widget->style->fg_gc[GTK_STATE_NORMAL]);
00398   }
00399   
00400 
00401   /* draw frame */
00402   gdk_gc_set_foreground(dgc, &(cols[C_WAVEFORM]));
00403   gdk_draw_rectangle(pixmap, dgc, FALSE,
00404                      scale_x_wave(0), scale_y_wave(max_level),
00405                      scale_x_wave(re->speechlen-1) - scale_x_wave(0),
00406                      scale_y_wave(-max_level) - scale_y_wave(max_level));
00407 
00408   if (sw_level_thres) {
00409     /* draw level threshold line */
00410     gdk_gc_set_foreground(dgc, &(cols[C_LEVELTHRES]));
00411     gdk_draw_line(pixmap, dgc,
00412                   scale_x_wave(0), scale_y_wave(re->jconf->detect.level_thres),
00413                   scale_x_wave(re->speechlen-1), scale_y_wave(re->jconf->detect.level_thres));
00414     gdk_draw_line(pixmap, dgc,
00415                   scale_x_wave(0), scale_y_wave(- re->jconf->detect.level_thres),
00416                   scale_x_wave(re->speechlen-1), scale_y_wave(- re->jconf->detect.level_thres));
00417     snprintf(buf, 20, "-lv %d", re->jconf->detect.level_thres);
00418     text_width = gdk_string_width(fontset, buf) + 1;
00419     gdk_draw_string(pixmap, fontset, dgc,
00420                     canvas_width - CANVAS_MARGIN - text_width - 2,
00421                     scale_y_wave(-max_level) - 2,
00422                     buf);
00423   }
00424   
00425   /* draw text */
00426   snprintf(buf, 20, "max: %d", max_level);
00427   text_width = gdk_string_width(fontset, buf) + 1;
00428   gdk_gc_set_foreground(dgc, &(cols[C_WAVEFORM]));
00429   gdk_draw_string(pixmap, fontset, dgc,
00430                   canvas_width - CANVAS_MARGIN - text_width - 2,
00431                   scale_y_wave(max_level) + 12,
00432                   buf);
00433 
00434   /* draw waveform */
00435   for(t=1;t<re->speechlen;t++) {
00436     gdk_gc_set_line_attributes(dgc, 1, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
00437     gdk_draw_line(pixmap, dgc,
00438                   scale_x_wave(t-1), scale_y_wave(re->speech[t-1]),
00439                   scale_x_wave(t), scale_y_wave(re->speech[t]));
00440   }
00441 }
00442 
00443 
00444 /**********************************************************************/
00445 /* GTK primitive functions to draw a trellis atom */
00446 /**********************************************************************/
00447 
00470 static void
00471 mygdk_draw_arc(GtkWidget *widget, int x1, int y1, int x2, int y2, int sw)
00472 {
00473   int width;
00474   UserColors c;
00475 
00476   /* first time, make gc for drawing */
00477   if (dgc == NULL) {
00478     dgc = gdk_gc_new(widget->window);
00479     gdk_gc_copy(dgc, widget->style->fg_gc[GTK_STATE_NORMAL]);
00480   }
00481 
00482   /* change arc style by sw */
00483   switch(sw) {
00484   case 0: c = C_LINE_FAINT; width = 1; break;
00485   case 1: c = C_LINE; width = 1; break;
00486   case 2: c = C_LINE_BEST; width = 3; break;
00487   case 3: c = C_PASS2_NEXT; width = 1; break; /* next */
00488   case 4: c = C_PASS2; width = 1; break; /* popper (realigned) */
00489   case 5: c = C_PASS2_NEXT; width = 2; break; /* popped (original) */
00490   case 6: c = C_PASS2_BEST; width = 3; break;
00491   default: c = C_LINE; width = 1; break;
00492   }
00493   
00494   /* draw arc line */
00495   if (sw_line) {
00496     gdk_gc_set_line_attributes(dgc, width, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
00497     gdk_gc_set_foreground(dgc, &(cols[c]));
00498     gdk_draw_line(pixmap, dgc, x1, y1, x2, y2);
00499     gdk_gc_set_line_attributes(dgc, 1, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
00500   }
00501 
00502   /* draw begin/end point rectangle */
00503   if (sw != 0 && sw != 5) {
00504     /* make edge */
00505     gdk_gc_set_foreground(dgc, &(cols[C_SHADOW]));
00506     gdk_draw_rectangle(pixmap, dgc,
00507                        TRUE,
00508                        x1 - width/2 - 2,
00509                        y1 - width/2 -2,
00510                        width+4,
00511                        width+4);
00512     gdk_draw_rectangle(pixmap, dgc,
00513                        TRUE,
00514                        x2 - width/2 - 2,
00515                        y2 - width/2 -2,
00516                        width+4,
00517                        width+4);
00518   }
00519   gdk_gc_set_foreground(dgc, &(cols[C_BGN]));
00520   gdk_draw_rectangle(pixmap, dgc,
00521                      TRUE,
00522                      x1 - width/2 - 1,
00523                      y1 - width/2 -1,
00524                      width+2,
00525                      width+2);
00526   if (c == C_LINE_BEST || c == C_PASS2_BEST) {
00527     gdk_gc_set_foreground(dgc, &(cols[C_END_BEST]));
00528   } else {
00529     gdk_gc_set_foreground(dgc, &(cols[C_END]));
00530   }
00531   gdk_draw_rectangle(pixmap, dgc,
00532                      TRUE,
00533                      x2 - width/2 - 1,
00534                      y2 - width/2 - 1,
00535                      width+2,
00536                      width+2);
00537   
00538 }
00539 
00558 static void
00559 draw_atom_sub(GtkWidget *widget, TRELLIS_ATOM *tre, TRELLIS_ATOM *last_tre, int sw)
00560 {
00561   int from_t;
00562   LOGPROB from_s;
00563   int from_w;
00564   
00565   /* draw word arc */
00566   if (sw_wid_axis) {
00567     if (tre->begintime <= 0) {
00568       from_t = 0;
00569       from_w = WORD_INVALID;
00570     } else {
00571       from_t = last_tre->endtime;
00572       from_w = last_tre->wid;
00573     }
00574     mygdk_draw_arc(widget,
00575                    scale_x(from_t),
00576                    scale_y_wid(from_w),
00577                    scale_x(tre->endtime),
00578                    scale_y_wid(tre->wid),
00579                    sw);
00580   } else {
00581     if (tre->begintime <= 0) {
00582       from_t = 0;
00583       from_s = 0.0;
00584     } else {
00585       from_t = last_tre->endtime;
00586       from_s = last_tre->backscore;
00587     }
00588     mygdk_draw_arc(widget,
00589                    scale_x(from_t),
00590                    scale_y(from_s, from_t),
00591                    scale_x(tre->endtime),
00592                    scale_y(tre->backscore, tre->endtime),
00593                    sw);
00594   }
00595 }
00596 
00613 static void
00614 draw_atom(GtkWidget *widget, TRELLIS_ATOM *tre, int sw)
00615 {
00616   draw_atom_sub(widget, tre, tre->last_tre, sw);
00617 }
00618 
00619 /* draw word output text */
00636 static void
00637 draw_atom_text(GtkWidget *widget, TRELLIS_ATOM *tre, int sw)
00638 {
00639   gint text_width;
00640   UserColors c;
00641   int style;
00642   int dx, dy, x, y;
00643   WORD_INFO *winfo;
00644 
00645   winfo = re->model->winfo;
00646   
00647   if (winfo->woutput[tre->wid] != NULL && strlen(winfo->woutput[tre->wid]) > 0) {
00648     switch(sw) {
00649     case 0: style = -1; break;
00650     case 1: c = C_TEXT; style = 0; break;
00651     case 2: c = C_TEXT_BEST; style = 1; break;
00652     case 3: c = C_PASS2_NEXT; style = 0; break;
00653     case 4: c = C_PASS2; style = 0; break;
00654     case 5: c = C_PASS2; style = 1; break;
00655     case 6: c = C_PASS2_BEST; style = 1; break;
00656     default: c = C_TEXT; style = 0; break;
00657     }
00658     if (style == -1) return;    /* do not draw text */
00659     
00660     text_width = gdk_string_width(fontset, winfo->woutput[tre->wid]) + 1;
00661     x = scale_x(tre->endtime) - text_width;
00662     if (sw_wid_axis) {
00663       y = scale_y_wid(tre->wid) - 4;
00664     } else {
00665       y = scale_y(tre->backscore, tre->endtime) - 4;
00666     }
00667     
00668     if (style == 1) {           /* make edge */
00669       gdk_gc_set_foreground(dgc, &cols[C_SHADOW]);
00670       for (dx = -1; dx <= 1; dx++) {
00671         for (dy = -1; dy <= 1; dy++) {
00672           if (dx == 0 && dy == 0) continue;
00673           gdk_draw_string(pixmap, fontset, dgc, x + dx, y + dy, 
00674                           winfo->woutput[tre->wid]);
00675         }
00676       }
00677     }
00678     gdk_gc_set_foreground(dgc, &cols[c]);
00679     gdk_draw_string(pixmap, fontset, dgc, x, y, 
00680                     winfo->woutput[tre->wid]);
00681   }
00682 }
00683 
00684 
00685 /**********************************************************************/
00686 /* wrapper for narrowing atoms to be drawn */
00687 /**********************************************************************/
00688 static WORD_ID *wordlist = NULL; 
00689 static WORD_ID wordlistnum = 0; 
00690 
00707 static boolean 
00708 wordlist_find(WORD_ID wid)
00709 {
00710   int left, right, mid;
00711   
00712   if (wordlistnum == 0) return FALSE;
00713 
00714   left = 0;
00715   right = wordlistnum - 1;
00716   while (left < right) {
00717     mid = (left + right) / 2;
00718     if (wordlist[mid] < wid) {
00719       left = mid + 1;
00720     } else {
00721       right = mid;
00722     }
00723   }
00724   if (wordlist[left] == wid) return TRUE;
00725   return FALSE;
00726 }
00727 
00728 
00729 /**********************************************************************/
00730 /* Top trellis atom drawing functions including wrapper */
00731 /* All functions below should call this */
00732 /**********************************************************************/
00733 
00750 static void
00751 draw_atom_top(GtkWidget *widget, TRELLIS_ATOM *tre, int sw)
00752 {
00753   if (wordlistnum == 0 || wordlist_find(tre->wid)) {
00754     draw_atom(widget, tre, sw);
00755   }
00756 }
00757 
00776 static void
00777 draw_atom_text_top(GtkWidget *widget, TRELLIS_ATOM *tre, int sw)
00778 {
00779   if (wordlistnum == 0 || wordlist_find(tre->wid)) {
00780     draw_atom_text(widget, tre, sw);
00781   }
00782 }
00783 
00784 
00785 /**********************************************************************/
00786 /* Draw a set of atom according to their properties */
00787 /**********************************************************************/
00788 
00801 static void
00802 draw_all_atom(GtkWidget *widget)
00803 {
00804   int t, i;
00805   TRELLIS_ATOM *tre;
00806   
00807   for (t=0;t<btlocal->framelen;t++) {
00808     for (i=0;i<btlocal->num[t];i++) {
00809       tre = btlocal->rw[t][i];
00810       draw_atom_top(widget, tre, 0);
00811     }
00812   }
00813   if (sw_text) {
00814     for (t=0;t<btlocal->framelen;t++) {
00815       for (i=0;i<btlocal->num[t];i++) {
00816         tre = btlocal->rw[t][i];
00817         draw_atom_text_top(widget, tre, 0);
00818       }
00819     }
00820   }
00821 }
00822 
00835 static void
00836 draw_context_valid_atom(GtkWidget *widget)
00837 {
00838   int t, i;
00839   TRELLIS_ATOM *tre;
00840 
00841   for (t=0;t<btlocal->framelen;t++) {
00842     for (i=0;i<btlocal->num[t];i++) {
00843       tre = btlocal->rw[t][i];
00844       if (tre->last_tre != NULL && tre->last_tre->wid != WORD_INVALID) {
00845         draw_atom_top(widget, tre->last_tre, 1);
00846       }
00847     }
00848   }
00849   if (sw_text) {
00850     for (t=0;t<btlocal->framelen;t++) {
00851       for (i=0;i<btlocal->num[t];i++) {
00852         tre = btlocal->rw[t][i];
00853         if (tre->last_tre != NULL && tre->last_tre->wid != WORD_INVALID) {
00854           draw_atom_text_top(widget, tre->last_tre, 1);
00855         }
00856       }
00857     }
00858   }
00859 }
00860 
00861 #ifdef WORD_GRAPH
00862 
00874 static void
00875 draw_word_graph(GtkWidget *widget)
00876 {
00877   int t, i;
00878   TRELLIS_ATOM *tre;
00879 
00880   /* assume (1)word atoms in word graph are already marked in
00881      generate_lattice() in beam.c, and (2) backtrellis is wid-sorted */
00882   for (t=0;t<btlocal->framelen;t++) {
00883     for (i=0;i<btlocal->num[t];i++) {
00884       tre = btlocal->rw[t][i];
00885       if (tre->within_wordgraph) {
00886         draw_atom_top(widget, tre, 1);
00887       }
00888     }
00889   }
00890   if (sw_text) {
00891     for (t=0;t<btlocal->framelen;t++) {
00892       for (i=0;i<btlocal->num[t];i++) {
00893         tre = btlocal->rw[t][i];
00894         if (tre->within_wordgraph) {
00895           draw_atom_text_top(widget, tre, 1);
00896         }
00897       }
00898     }
00899   }
00900   
00901 }
00902 #endif
00903 
00916 static void
00917 draw_best_path(GtkWidget *widget)
00918 {
00919   int last_time;
00920   LOGPROB maxscore;
00921   TRELLIS_ATOM *tre, *last_tre;
00922   int i;
00923 
00924   /* look for the beginning trellis word at end of speech */
00925   for (last_time = btlocal->framelen - 1; last_time >= 0; last_time--) {
00926 #ifdef USE_NGRAM
00927     /* it is fixed to the tail silence model (winfo->tail_silwid) */
00928     last_tre = bt_binsearch_atom(btlocal, last_time, re->model->winfo->tail_silwid);
00929     if (last_tre != NULL) break;
00930 #else /* USE_DFA */
00931     /* the best trellis word on the last frame (not use cp_end[]) */
00932     maxscore = LOG_ZERO;
00933     for (i=0;i<btlocal->num[last_time];i++) {
00934       tre = btlocal->rw[last_time][i];
00935       /*      if (dfa->cp_end[winfo->wton[tmp->wid]] == TRUE) {*/
00936         if (maxscore < tre->backscore) {
00937           maxscore = tre->backscore;
00938           last_tre = tre;
00939         }
00940         /*      }*/
00941     }
00942     if (maxscore != LOG_ZERO) break;
00943 #endif
00944   }
00945   if (last_time < 0) return;            /* last_tre not found */
00946 
00947   /* parse from the beginning word to find the best path */
00948   draw_atom_top(widget, last_tre, 2);
00949   tre = last_tre;
00950   while (tre->begintime > 0) {
00951     tre = tre->last_tre;
00952     draw_atom_top(widget, tre, 2);
00953   }
00954   if (sw_text) {
00955     draw_atom_text_top(widget, last_tre, 2);
00956     tre = last_tre;
00957     while (tre->begintime > 0) {
00958       tre = tre->last_tre;
00959       draw_atom_text_top(widget, tre, 2);
00960     }
00961   }
00962 }
00963 
00964 
00965 /**********************************************************************/
00966 /* 2nd pass drawing data collection functions */
00967 /* will be called from search_bestfirst_main.c to gather the atoms
00968    referred to in the search process of the 2nd pass */
00969 /**********************************************************************/
00970 
00983 void
00984 visual2_init(int maxhypo)
00985 {
00986   POPNODE *p, *ptmp;
00987   int i;
00988 
00989   if (popped == NULL) {
00990     popped = (POPNODE *)mymalloc(sizeof(POPNODE) * (maxhypo + 1));
00991   } else {
00992     for(i=0;i<pnum;i++) {
00993       p = popped[i].next;
00994       while(p) {
00995         ptmp = p->next;
00996         free(p);
00997         p = ptmp;
00998       }
00999     }
01000   }
01001   pnum = 1;
01002   /* for start words */
01003   popped[0].tre = NULL;
01004   popped[0].score = LOG_ZERO;
01005   popped[0].last = NULL;
01006   popped[0].next = NULL;
01007 
01008   /* for bests */
01009   p = lastpop;
01010   while(p) {
01011     ptmp = p->next;
01012     free(p);
01013     p = ptmp;
01014   }
01015   lastpop = NULL;
01016 }
01017 
01032 void
01033 visual2_popped(NODE *n, int popctr)
01034 {
01035   if (pnum < popctr + 1) pnum = popctr + 1;
01036   
01037   popped[popctr].tre = n->popnode->tre;
01038   popped[popctr].score = n->popnode->score;
01039   popped[popctr].last = n->popnode->last;
01040   popped[popctr].next = NULL;
01041 
01042   n->popnode = &(popped[popctr]);
01043 }
01044   
01061 void
01062 visual2_next_word(NODE *next, NODE *prev, int popctr)
01063 {
01064   POPNODE *new;
01065 
01066   /* allocate new popnode info */
01067   new = (POPNODE *)mymalloc(sizeof(POPNODE));
01068   new->tre = next->tre;
01069   new->score = next->score;
01070   /* link between previous POPNODE */
01071   new->last = (prev) ? prev->popnode : NULL;
01072   next->popnode = new;
01073   /* store */
01074   new->next = popped[popctr].next;
01075   popped[popctr].next = new;
01076 }
01077 
01092 void
01093 visual2_best(NODE *now, WORD_INFO *winfo)
01094 {
01095   POPNODE *new;
01096   
01097   new = (POPNODE *)mymalloc(sizeof(POPNODE));
01098   new->tre = now->popnode->tre;
01099   new->score = now->popnode->score;
01100   new->last = now->popnode->last;
01101   new->next = lastpop;
01102   lastpop = new;
01103 }
01104 
01105 /**********************************************************************/
01106 /* Draw atoms refered at the 2nd pass */
01107 /**********************************************************************/
01108 
01109 /* draw 2nd pass results */
01122 static void
01123 draw_final_results(GtkWidget *widget)
01124 {
01125   POPNODE *firstp, *lastp, *p;
01126 
01127   for(firstp = lastpop; firstp; firstp = firstp->next) {
01128     if (firstp->tre != NULL) {
01129       draw_atom(widget, firstp->tre, 6);
01130     }
01131     lastp = firstp;
01132     for(p = firstp->last; p; p = p->last) {
01133       if (p->tre != NULL) {
01134         draw_atom_sub(widget, p->tre, lastp->tre, 6);
01135         draw_atom_text_top(widget, p->tre, 6);
01136       }
01137       lastp = p;
01138     }
01139   }
01140 }
01141 
01142 static LOGPROB maxscore;        
01143 static LOGPROB minscore;        
01144 
01154 static void
01155 get_max_hypo_score()
01156 {
01157   POPNODE *p;
01158   int i;
01159 
01160   maxscore = LOG_ZERO;
01161   minscore = 0.0;
01162   for(i=1;i<pnum;i++) {
01163     if (maxscore < popped[i].score) maxscore = popped[i].score;
01164     if (minscore > popped[i].score) minscore = popped[i].score;
01165   }
01166 }
01167 
01184 static gint
01185 scale_hypo_y(LOGPROB s)
01186 {
01187   gint y;
01188   gint yoffset, height;
01189 
01190   yoffset = CANVAS_MARGIN + RESULT_HEIGHT + (re->speechlen != 0 ? (WAVE_MARGIN + WAVE_HEIGHT) : 0);
01191   height = canvas_height - yoffset - CANVAS_MARGIN;
01192   y = (maxscore - s) * height / (maxscore - minscore) + yoffset;
01193   return(y);
01194 }
01195 
01196 /* draw popped words */
01217 static void
01218 draw_popped(GtkWidget *widget, POPNODE *p, UserColors c, int width, int style)
01219 {
01220   int text_width;
01221   gint x, y;
01222 
01223   if (p->tre == NULL) return;
01224 
01225   if (p->last != NULL && p->last->tre != NULL) {
01226     gdk_gc_set_line_attributes(dgc, width, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
01227     gdk_gc_set_foreground(dgc, &(cols[c]));
01228     gdk_draw_line(pixmap, dgc,
01229                   scale_x(p->last->tre->endtime),
01230                   scale_hypo_y(p->last->score),
01231                   scale_x(p->tre->endtime),
01232                   scale_hypo_y(p->score));
01233     gdk_gc_set_line_attributes(dgc, 1, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
01234   }
01235   
01236   if (p->tre != NULL) {
01237     x = scale_x(p->tre->endtime);
01238     y = scale_hypo_y(p->score);
01239     if (style == 1) {
01240       gdk_gc_set_foreground(dgc, &(cols[C_SHADOW]));
01241       gdk_draw_rectangle(pixmap, dgc,
01242                          TRUE,
01243                          x - 3,
01244                          y - 3,
01245                          7,
01246                          7);
01247     } else if (style == 2) {
01248       gdk_gc_set_foreground(dgc, &(cols[c]));
01249       gdk_draw_rectangle(pixmap, dgc,
01250                          TRUE,
01251                          x - 3,
01252                          y - 3,
01253                          7,
01254                          7);
01255     }
01256     gdk_gc_set_foreground(dgc, &(cols[c]));
01257     gdk_draw_rectangle(pixmap, dgc,
01258                        TRUE,
01259                        x - 2,
01260                        y - 2,
01261                        5,
01262                        5);
01263     if (p->tre->wid != WORD_INVALID) {
01264       text_width = gdk_string_width(fontset, re->model->winfo->woutput[p->tre->wid]) + 1;
01265       gdk_draw_string(pixmap, fontset, dgc, x - text_width-1, y - 5, 
01266                       re->model->winfo->woutput[p->tre->wid]);
01267     }
01268   }
01269     
01270   
01271 }
01272 
01273 /* draw popped words at one hypothesis expantion */
01274 
01275 static int old_popctr;          
01276 
01291 static void
01292 draw_popnodes(GtkWidget *widget, int popctr)
01293 {
01294   POPNODE *p, *porg;
01295 
01296   if (popctr < 0 || popctr >= pnum) {
01297     fprintf(stderr, "invalid popctr (%d > %d)!\n", popctr, pnum);
01298     return;
01299   }
01300   
01301   porg = &(popped[popctr]);
01302 
01303   /* draw expanded atoms */
01304   for(p = porg->next; p; p = p->next) {
01305     draw_popped(widget, p, C_LINE_BEST, 1, 0);
01306   }
01307 
01308   /* draw hypothesis context */
01309   for(p = porg->last; p; p = p->last) {
01310     draw_popped(widget, p, C_PASS2_BEST, 2, 0);
01311   }
01312   draw_popped(widget, porg, C_PASS2_BEST, 3, 1);
01313 
01314   old_popctr = popctr;
01315 }
01328 static void
01329 draw_popnodes_old(GtkWidget *widget)
01330 {
01331   POPNODE *p, *porg;
01332 
01333   porg = &(popped[old_popctr]);
01334 
01335   /* draw expanded atoms */
01336   for(p = porg->next; p; p = p->next) {
01337     draw_popped(widget, p, C_LINE_FAINT, 1, 0);
01338   }
01339 
01340   /* draw hypothesis context */
01341   for(p = porg->last; p; p = p->last) {
01342     draw_popped(widget, p, C_PASS2, 2, 0);
01343   }
01344   draw_popped(widget, porg, C_PASS2, 3, 2);
01345 }
01346 
01347 /**********************************************************************/
01348 /* GTK TopLevel draw/redraw functions */
01349 /* will be called for each exposure/configure event */
01350 /**********************************************************************/
01351 static boolean fitscreen = TRUE; 
01352 
01365 static void
01366 draw_background(GtkWidget *widget)
01367 {
01368   static char buf[MAX_HMMNAME_LEN];
01369   
01370   /* first time, make gc for drawing */
01371   if (dgc == NULL) {
01372     dgc = gdk_gc_new(widget->window);
01373     gdk_gc_copy(dgc, widget->style->fg_gc[GTK_STATE_NORMAL]);
01374   }
01375   /* clear pixmap background */
01376   gdk_gc_set_foreground(dgc, &(cols[C_BG]));
01377   gdk_draw_rectangle(pixmap, dgc, TRUE,
01378                      0,0,canvas_width,canvas_height);
01379 
01380   /* display view mode and zoom status */
01381   gdk_gc_set_foreground(dgc, &(cols[C_TEXT]));
01382   if (sw_hypo) {
01383     gdk_draw_string(pixmap, fontset, dgc, 0, canvas_height - 16,
01384                     "Hypothesis score (2nd pass)");
01385   } else {
01386     gdk_draw_string(pixmap, fontset, dgc, 0, canvas_height - 16,
01387                     sw_wid_axis ? "Word ID" : (sw_score_beam ? "Beam score" : "Accumulated score (normalized by time)"));
01388   }
01389   snprintf(buf, 50, "x%3.1f", (float)canvas_width / (float)btlocal->framelen);
01390   gdk_draw_string(pixmap, fontset, dgc, 0, canvas_height - 3, buf);
01391 }
01392 
01405 static void
01406 drawarea_draw(GtkWidget *widget)
01407 {
01408   /* allocate (new) pixmap */
01409   if (pixmap) {
01410     gdk_pixmap_unref(pixmap);   /* destroy old one */
01411   }
01412   pixmap = gdk_pixmap_new(widget->window, canvas_width, canvas_height, -1);
01413   
01414   /* make background */
01415   draw_background(widget);
01416 
01417   if (re->speechlen != 0) {
01418     draw_waveform(widget);
01419   }
01420 
01421   if (!sw_hypo) {
01422     if (btlocal != NULL) {
01423       /* draw objects */
01424       draw_all_atom(widget);
01425 #ifdef WORD_GRAPH
01426       draw_word_graph(widget);
01427 #else
01428       draw_context_valid_atom(widget);
01429 #endif
01430       draw_best_path(widget);
01431     }
01432     if (popped != NULL) {
01433       /* draw 2nd pass objects */
01434       draw_final_results(widget);
01435     }
01436   }
01437 }
01438 
01451 static void
01452 drawarea_expose(GtkWidget *widget)
01453 {
01454   GdkRectangle r;
01455 
01456   r.x = 0;
01457   r.y = 0;
01458   r.width = canvas_width;
01459   r.height = canvas_height;
01460   gtk_widget_draw(widget, &r);
01461 }
01462 
01483 static gboolean
01484 event_drawarea_expose(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
01485 {
01486   if (pixmap != NULL) {
01487     gdk_draw_pixmap(widget->window,
01488                     widget->style->fg_gc[GTK_STATE_NORMAL],
01489                     pixmap,
01490                     event->area.x, event->area.y,
01491                     event->area.x, event->area.y,
01492                     event->area.width, event->area.height);
01493   }
01494 }
01495 
01516 static gboolean
01517 event_drawarea_configure(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
01518 {
01519   if (fitscreen) {              /* if in zoom mode, resizing window does not cause resize of the canvas */
01520     canvas_width = widget->allocation.width; /* get canvas size */
01521   }
01522   /* canvas height will be always automatically changed by resizing */
01523   canvas_height = widget->allocation.height;
01524 
01525   /* redraw objects to pixmap */
01526   drawarea_draw(widget);
01527 }
01528 
01529 
01530 /**********************************************************************/
01531 /* GTK callbacks for buttons */
01532 /**********************************************************************/
01533 
01548 static void
01549 action_toggle_thres(GtkWidget *widget)
01550 {
01551 
01552   if (re->speechlen == 0) return;
01553   /* toggle switch */
01554   if (sw_level_thres) sw_level_thres = FALSE;
01555   else sw_level_thres = TRUE;
01556 
01557   /* redraw objects to pixmap */
01558   drawarea_draw(widget);
01559 
01560   /* tell X to issue expose event on this window */
01561   drawarea_expose(widget);
01562 }
01563 
01564 #ifdef PLAYCOMMAND
01565 
01577 static void
01578 action_play_waveform(GtkWidget *widget)
01579 {
01580   char buf[80];
01581   static char command[250];
01582   int fd;
01583 
01584   if (re->speechlen == 0) return;
01585   
01586   /* play waveform */
01587   snprintf(buf, 250, "/var/tmp/julius_visual_play.%d", getpid());
01588   if ((fd = open(buf, O_CREAT | O_TRUNC | O_WRONLY, 0644)) < 0) {
01589     fprintf(stderr, "cannot open %s for writing\n", buf);
01590     return;
01591   }
01592   if (wrsamp(fd, re->speech, re->speechlen) < 0) {
01593     fprintf(stderr, "failed to write to %s for playing\n", buf);
01594     return;
01595   }
01596   close(fd);
01597   
01598   snprintf(command, 250, PLAYCOMMAND, re->jconf->analysis.para.smp_freq, buf);
01599   printf("play: [%s]\n", command);
01600   system(command);
01601 
01602   unlink(buf);
01603 }
01604 #endif
01605 
01620 static void
01621 action_view_wid(GtkWidget *button, GtkWidget *widget)
01622 {
01623   if (GTK_TOGGLE_BUTTON(button)->active) {
01624     /* set switch */
01625     sw_wid_axis = TRUE;
01626     sw_hypo = FALSE;
01627     /* redraw objects to pixmap */
01628     drawarea_draw(widget);
01629     /* tell X to issue expose event on this window */
01630     drawarea_expose(widget);
01631   } else {
01632     sw_wid_axis = FALSE;
01633   }
01634 }
01635 
01650 static void
01651 action_view_score(GtkWidget *button, GtkWidget *widget)
01652 {
01653   if (GTK_TOGGLE_BUTTON(button)->active) {
01654     /* set switch */
01655     sw_score_beam = FALSE;
01656     sw_hypo = FALSE;
01657     /* redraw objects to pixmap */
01658     drawarea_draw(widget);
01659     /* tell X to issue expose event on this window */
01660     drawarea_expose(widget);
01661   }
01662 }
01663 
01680 static void
01681 action_view_beam(GtkWidget *button, GtkWidget *widget)
01682 {
01683   if (GTK_TOGGLE_BUTTON(button)->active) {
01684     /* set switch */
01685     sw_score_beam = TRUE;
01686     sw_hypo = FALSE;
01687     /* redraw objects to pixmap */
01688     drawarea_draw(widget);
01689     /* tell X to issue expose event on this window */
01690     drawarea_expose(widget);
01691   }
01692 }
01693 
01710 static void
01711 action_toggle_arc(GtkWidget *button, GtkWidget *widget)
01712 {
01713   if (GTK_TOGGLE_BUTTON(button)->active) {
01714     sw_text = TRUE;
01715     sw_line = TRUE;
01716   } else {
01717     sw_text = FALSE;
01718     sw_line = FALSE;
01719   }
01720   /* redraw objects to pixmap */
01721   drawarea_draw(widget);
01722   /* tell X to issue expose event on this window */
01723   drawarea_expose(widget);
01724 }
01725 
01742 static void
01743 action_set_wid(GtkWidget *widget, GtkWidget *draw)
01744 {
01745   gchar *entry_text;
01746   WORD_ID i;
01747   
01748   entry_text = gtk_entry_get_text(GTK_ENTRY(widget));
01749 
01750   /* allocate */
01751   if (wordlist == NULL) {
01752     wordlist = mymalloc(sizeof(WORD_ID) * re->model->winfo->num);
01753   }
01754   wordlistnum = 0;
01755 
01756   /* pickup words with the specified output text and regiter them to the lsit */
01757   if (strlen(entry_text) == 0) {
01758     wordlistnum = 0;
01759   } else {
01760     for (i=0;i<re->model->winfo->num;i++) {
01761       if (strmatch(entry_text, re->model->winfo->woutput[i])) {
01762         wordlist[wordlistnum] = i;
01763         wordlistnum++;
01764       }
01765     }
01766     if (wordlistnum == 0) {
01767       fprintf(stderr, "word \"%s\" not found, show all\n", entry_text);
01768     } else {
01769       fprintf(stderr, "%d words found for \"%s\"\n", wordlistnum, entry_text);
01770     }
01771   }
01772   
01773   /* redraw objects to pixmap */
01774   drawarea_draw(draw);
01775   /* tell X to issue expose event on this window */
01776   drawarea_expose(draw);
01777 }
01778 
01793 static void
01794 action_zoom(GtkWidget *widget)
01795 {
01796   fitscreen = FALSE;
01797   if (btlocal != NULL) {
01798     canvas_width = btlocal->framelen * 2;
01799     gtk_drawing_area_size(GTK_DRAWING_AREA(widget), canvas_width, canvas_height);
01800 
01801   }
01802   drawarea_draw(widget);
01803   drawarea_expose(widget);
01804 }
01805 
01820 static void
01821 action_zoom_4(GtkWidget *widget)
01822 {
01823   fitscreen = FALSE;
01824   if (btlocal != NULL) {
01825     canvas_width = btlocal->framelen * 4;
01826     gtk_drawing_area_size(GTK_DRAWING_AREA(widget), canvas_width, canvas_height);
01827   }
01828  
01829   drawarea_draw(widget);
01830   drawarea_expose(widget);
01831 }
01832 
01847 static void
01848 action_zoom_8(GtkWidget *widget)
01849 {
01850   fitscreen = FALSE;
01851   if (btlocal != NULL) {
01852     canvas_width = btlocal->framelen * 8;
01853     gtk_drawing_area_size(GTK_DRAWING_AREA(widget), canvas_width, canvas_height);
01854   }
01855   
01856   drawarea_draw(widget);
01857   drawarea_expose(widget);
01858 }
01859 
01874 static void
01875 action_fit_screen(GtkWidget *widget)
01876 {
01877   fitscreen = TRUE;
01878   canvas_width = widget->parent->allocation.width;
01879   gtk_drawing_area_size(GTK_DRAWING_AREA(widget), canvas_width, canvas_height);
01880   
01881   drawarea_draw(widget);
01882   drawarea_expose(widget);
01883 }
01884 
01900 static void
01901 action_toggle_popctr(GtkWidget *button, GtkWidget *widget)
01902 {
01903   if (GTK_TOGGLE_BUTTON(button)->active) {
01904     sw_hypo = TRUE;
01905   } else {
01906     sw_hypo = FALSE;
01907   }
01908   drawarea_draw(widget);
01909   drawarea_expose(widget);
01910 }
01911 
01929 static void
01930 action_change_popctr(GtkAdjustment *adj, GtkWidget *widget)
01931 {
01932   int popctr;
01933 
01934   if (sw_hypo) {
01935     popctr = adj->value;
01936     draw_popnodes_old(widget);
01937     draw_popnodes(widget, popctr);
01938     drawarea_expose(widget);
01939   }
01940 }
01941 
01942 /**********************************************************************/
01943 /* GTK delete/destroy event handler */
01944 /**********************************************************************/
01945 
01966 static gint
01967 delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
01968 {
01969   return (FALSE);               /* emit destroy signal */
01970 }
01971 
01986 static void
01987 destroy(GtkWidget *widget, gpointer data)
01988 {
01989   gtk_main_quit();
01990 }
01991 
01992 /**********************************************************************/
01993 /* Main public functions for visualization */
01994 /**********************************************************************/
01995 
02004 void
02005 visual_init(Recog *recog)
02006 {
02007   POPNODE *p;
02008 
02009   /* hold recognition instance to local */
02010   re = recog;
02011 
02012   /* reset values */
02013   btlocal = NULL;
02014   
02015   /* initialize Gtk/Gdk libraries */
02016   /* no argument passed as gtk options */
02017   /*gtk_init (&argc, &argv);*/
02018   gtk_init(NULL, NULL);
02019 
02020   /* set locale */
02021   gtk_set_locale();
02022 
02023   /* load fontset */
02024   fontset = gdk_fontset_load(FONTSET);
02025   if (fontset == NULL) {
02026     fprintf(stderr, "cannot load X font \"%s\" for visualize\n", FONTSET); exit(-1);
02027   }
02028 
02029   /* initialize color */
02030   color_init();
02031 
02032   fprintf(stderr, "GTK initialized\n");
02033 
02034 }
02035 
02048 void
02049 visual_show(BACKTRELLIS *bt)
02050 {
02051   GtkWidget *window, *button, *draw, *entry, *scrolled_window, *scale;
02052   GtkWidget *box1, *box2, *label, *frame, *box3;
02053   GtkObject *adj;
02054   GSList *group;
02055   GList *glist;
02056 
02057   
02058   fprintf(stderr, "*** Showing word trellis view (close window to proceed)\n");
02059 
02060   /* store pointer to backtrellis data */
02061   btlocal = bt;
02062 
02063   /* prepare for Y axis score normalization */
02064   get_max_frame_score(bt);
02065 
02066   /* prepare for Y axis hypo score normalization */
02067   get_max_hypo_score();
02068 
02069   /* prepare for waveform */
02070   if (re->speechlen != 0) get_max_waveform_level();
02071 
02072   /* start with trellis view */
02073   sw_hypo = FALSE;
02074 
02075   /* reset value */
02076   fitscreen = TRUE;
02077   if (dgc != NULL) {
02078     gdk_gc_unref(dgc);
02079     dgc = NULL;
02080   }
02081 
02082   /* create main window */
02083   window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
02084   gtk_widget_set_usize(GTK_WIDGET(window), DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT);
02085   gtk_window_set_title(GTK_WINDOW(window), WINTITLE);
02086   gtk_signal_connect(GTK_OBJECT(window), "delete_event",
02087                      GTK_SIGNAL_FUNC(delete_event), NULL);
02088   gtk_signal_connect(GTK_OBJECT(window), "destroy",
02089                      GTK_SIGNAL_FUNC(destroy), NULL);
02090   gtk_container_border_width(GTK_CONTAINER(window), 10);
02091 
02092   /* create horizontal packing box */
02093   box1 = gtk_hbox_new(FALSE, 5);
02094   gtk_container_add(GTK_CONTAINER(window), box1);
02095 
02096   /* create scrolled window */
02097   scrolled_window = gtk_scrolled_window_new(NULL, NULL);
02098   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_ALWAYS,GTK_POLICY_AUTOMATIC);
02099 
02100   /* create drawing area */
02101   draw = gtk_drawing_area_new();
02102   /*  gtk_drawing_area_size(GTK_DRAWING_AREA(draw), DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT);*/
02103   gtk_signal_connect(GTK_OBJECT(draw), "expose-event", GTK_SIGNAL_FUNC(event_drawarea_expose), NULL);
02104   gtk_signal_connect(GTK_OBJECT(draw), "configure-event", GTK_SIGNAL_FUNC(event_drawarea_configure), NULL);
02105   gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window), draw);
02106   gtk_box_pack_start(GTK_BOX(box1), scrolled_window, TRUE, TRUE, 0);
02107 
02108   /* create packing box for buttons */
02109   box2 = gtk_vbox_new(FALSE, 5);
02110   gtk_box_pack_start(GTK_BOX(box1), box2, FALSE, TRUE, 0);
02111 
02112   if (re->speechlen != 0) {
02113     /* create waveform related frame */
02114     frame = gtk_frame_new("waveform");
02115     gtk_box_pack_start(GTK_BOX(box2), frame, FALSE, FALSE, 0);
02116     box3 = gtk_hbox_new(FALSE, 5);
02117     gtk_container_set_border_width(GTK_CONTAINER(box3), 5);
02118     gtk_container_add(GTK_CONTAINER(frame), box3);
02119     
02120     /* create play button if supported */
02121 #ifdef PLAYCOMMAND
02122     button = gtk_button_new_with_label("play");
02123     gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
02124                               GTK_SIGNAL_FUNC(action_play_waveform), GTK_OBJECT(draw));
02125     gtk_box_pack_start(GTK_BOX(box3), button, FALSE, FALSE, 0);
02126 #endif
02127     
02128     /* create level thres toggle button */
02129     button = gtk_button_new_with_label("thres");
02130     gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
02131                               GTK_SIGNAL_FUNC(action_toggle_thres), GTK_OBJECT(draw));
02132     gtk_box_pack_start(GTK_BOX(box3), button, FALSE, FALSE, 0);
02133   }
02134     
02135   /* create scaling frame */
02136   frame = gtk_frame_new("change view");
02137   gtk_box_pack_start(GTK_BOX(box2), frame, FALSE, FALSE, 0);
02138   box3 = gtk_hbox_new(FALSE, 5);
02139   gtk_container_set_border_width(GTK_CONTAINER(box3), 5);
02140   gtk_container_add(GTK_CONTAINER(frame), box3);
02141 
02142   /* create word view button */
02143   button = gtk_radio_button_new_with_label(NULL, "word");
02144   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
02145   gtk_signal_connect(GTK_OBJECT(button), "toggled",
02146                      GTK_SIGNAL_FUNC(action_view_wid), GTK_OBJECT(draw));
02147   gtk_box_pack_start(GTK_BOX(box3), button, FALSE, FALSE, 0);
02148 
02149   /* create score view button */
02150   group = gtk_radio_button_group(GTK_RADIO_BUTTON(button));
02151   button = gtk_radio_button_new_with_label(group, "score");
02152   gtk_signal_connect(GTK_OBJECT(button), "toggled",
02153                      GTK_SIGNAL_FUNC(action_view_score), GTK_OBJECT(draw));
02154   gtk_box_pack_start(GTK_BOX(box3), button, FALSE, FALSE, 0);
02155 
02156   /* create beam view button */
02157   group = gtk_radio_button_group(GTK_RADIO_BUTTON(button));
02158   button = gtk_radio_button_new_with_label(group, "beam");
02159   gtk_signal_connect(GTK_OBJECT(button), "toggled",
02160                      GTK_SIGNAL_FUNC(action_view_beam), GTK_OBJECT(draw));
02161   gtk_box_pack_start(GTK_BOX(box3), button, FALSE, FALSE, 0);
02162 
02163   /* create show/hide frame */
02164   frame = gtk_frame_new("show/hide");
02165   gtk_box_pack_start(GTK_BOX(box2), frame, FALSE, FALSE, 0);
02166   box3 = gtk_vbox_new(FALSE, 5);
02167   gtk_container_set_border_width(GTK_CONTAINER(box3), 5);
02168   gtk_container_add(GTK_CONTAINER(frame), box3);
02169 
02170   /* create text toggle button */
02171   button = gtk_toggle_button_new_with_label("arcs");
02172   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
02173   gtk_signal_connect(GTK_OBJECT(button), "toggled",
02174                      GTK_SIGNAL_FUNC(action_toggle_arc), GTK_OBJECT(draw));
02175   gtk_box_pack_start(GTK_BOX(box3), button, FALSE, FALSE, 0);
02176 
02177   /* create word entry frame */
02178   frame = gtk_frame_new("view words");
02179   gtk_box_pack_start(GTK_BOX(box2), frame, FALSE, FALSE, 0);
02180   box3 = gtk_vbox_new(FALSE, 5);
02181   gtk_container_set_border_width(GTK_CONTAINER(box3), 5);
02182   gtk_container_add(GTK_CONTAINER(frame), box3);
02183 
02184   /* create word ID entry */
02185   entry = gtk_entry_new_with_max_length(16);
02186   gtk_signal_connect(GTK_OBJECT(entry), "activate",
02187                      GTK_SIGNAL_FUNC(action_set_wid), GTK_OBJECT(draw));
02188   gtk_box_pack_start(GTK_BOX(box3), entry, FALSE, FALSE, 0);
02189 
02190   /* create zoom frame */
02191   frame = gtk_frame_new("zoom");
02192   gtk_box_pack_start(GTK_BOX(box2), frame, FALSE, FALSE, 0);
02193   box3 = gtk_hbox_new(FALSE, 5);
02194   gtk_container_set_border_width(GTK_CONTAINER(box3), 5);
02195   gtk_container_add(GTK_CONTAINER(frame), box3);
02196 
02197   /* create x zoom button */
02198   button = gtk_button_new_with_label("x2");
02199   gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
02200                             GTK_SIGNAL_FUNC(action_zoom), GTK_OBJECT(draw));
02201   gtk_box_pack_start(GTK_BOX(box3), button, FALSE, FALSE, 0);
02202 
02203   /* create x zoom button */
02204   button = gtk_button_new_with_label("x4");
02205   gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
02206                             GTK_SIGNAL_FUNC(action_zoom_4), GTK_OBJECT(draw));
02207   gtk_box_pack_start(GTK_BOX(box3), button, FALSE, FALSE, 0);
02208 
02209   /* create x more zoom button */
02210   button = gtk_button_new_with_label("x8");
02211   gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
02212                             GTK_SIGNAL_FUNC(action_zoom_8), GTK_OBJECT(draw));
02213   gtk_box_pack_start(GTK_BOX(box3), button, FALSE, FALSE, 0);
02214 
02215   /* create fit screen button */
02216   button = gtk_button_new_with_label("fit");
02217   gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
02218                             GTK_SIGNAL_FUNC(action_fit_screen), GTK_OBJECT(draw));
02219   gtk_box_pack_start(GTK_BOX(box3), button, FALSE, FALSE, 0);
02220 
02221   /* create replay frame */
02222   frame = gtk_frame_new("pass2 replay");
02223   gtk_box_pack_start(GTK_BOX(box2), frame, FALSE, FALSE, 0);
02224   box3 = gtk_vbox_new(FALSE, 5);
02225   gtk_container_set_border_width(GTK_CONTAINER(box3), 5);
02226   gtk_container_add(GTK_CONTAINER(frame), box3);
02227 
02228   adj = gtk_adjustment_new(0.0, 0.0, (pnum-1) + 5, 1.0, 1.0, 5.0);
02229   gtk_signal_connect(GTK_OBJECT(adj), "value_changed",
02230                      GTK_SIGNAL_FUNC(action_change_popctr), GTK_OBJECT(draw));
02231 
02232   /* create replay start button */
02233   button = gtk_toggle_button_new_with_label("start");
02234   gtk_signal_connect(GTK_OBJECT(button), "toggled",
02235                      GTK_SIGNAL_FUNC(action_toggle_popctr), GTK_OBJECT(draw));
02236   gtk_box_pack_start(GTK_BOX(box3), button, FALSE, FALSE, 0);
02237   
02238   /* create replay scale widget */
02239   scale = gtk_hscale_new(GTK_ADJUSTMENT(adj));
02240   gtk_scale_set_digits(GTK_SCALE(scale), 0);
02241   gtk_box_pack_start(GTK_BOX(box3), scale, FALSE, FALSE, 0);
02242   
02243   /* create close button */
02244   button = gtk_button_new_with_label("close");
02245   /* connect click event to close the window */
02246   gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
02247                             GTK_SIGNAL_FUNC(gtk_widget_destroy),
02248                             GTK_OBJECT(window));
02249   
02250   gtk_box_pack_start(GTK_BOX(box2), button, FALSE, FALSE, 0);
02251 
02252   /* show all the widget */
02253   gtk_widget_show_all(window);
02254 
02255   /* enter the gtk event routine */
02256   gtk_main();
02257 }
02258 
02259 #endif /* VISUALIZE */