Julius 4.2
libjulius/src/spsegment.c
説明を見る。
00001 
00064 /*
00065  * Copyright (c) 1991-2011 Kawahara Lab., Kyoto University
00066  * Copyright (c) 2000-2005 Shikano Lab., Nara Institute of Science and Technology
00067  * Copyright (c) 2005-2011 Julius project team, Nagoya Institute of Technology
00068  * All rights reserved
00069  */
00070 
00071 #include <julius/julius.h>
00072 
00073 
00097 boolean
00098 is_sil(WORD_ID w, RecogProcess *r)
00099 {
00100   WORD_INFO *winfo;
00101   HTK_HMM_INFO *hmm;
00102   int i;
00103 
00104   winfo = r->lm->winfo;
00105   hmm = r->am->hmminfo;
00106 
00107   /* num of phones should be 1 */
00108   if (winfo->wlen[w] > 1) return FALSE;
00109 
00110   if (r->pass1.pausemodel) {
00111     /* has pause model list */
00112     for(i=0;i<r->pass1.pausemodelnum;i++) {
00113       if (strmatch(winfo->wseq[w][0]->name, r->pass1.pausemodel[i])) {
00114         return TRUE;
00115       }
00116     }
00117   } else {
00118     /* short pause (specified by "-spmodel") */
00119     if (winfo->wseq[w][0] == hmm->sp) return TRUE;
00120     
00121     if (r->lmtype == LM_PROB) {
00122       /* head/tail sil */
00123       if (w == winfo->head_silwid || w == winfo->tail_silwid) return TRUE;
00124     }
00125   }
00126 
00127   return FALSE;
00128 }
00129 
00153 void
00154 mfcc_copy_to_rest_and_shrink(MFCCCalc *mfcc, int start, int end)
00155 {
00156   int t;
00157 
00158   /* copy rest parameters for next process */
00159   mfcc->rest_param = new_param();
00160   memcpy(&(mfcc->rest_param->header), &(mfcc->param->header), sizeof(HTK_Param_Header));
00161   mfcc->rest_param->samplenum = mfcc->param->samplenum - start;
00162   mfcc->rest_param->header.samplenum = mfcc->rest_param->samplenum;
00163   mfcc->rest_param->veclen = mfcc->param->veclen;
00164   if (param_alloc(mfcc->rest_param, mfcc->rest_param->samplenum, mfcc->rest_param->veclen) == FALSE) {
00165     j_internal_error("ERROR: segmented: failed to allocate memory for rest param\n");
00166   }
00167   /* copy data */
00168   for(t=start;t<mfcc->param->samplenum;t++) {
00169     memcpy(mfcc->rest_param->parvec[t-start], mfcc->param->parvec[t], sizeof(VECT) * mfcc->rest_param->veclen);
00170   }
00171   
00172   /* shrink original param */
00173   /* just shrink the length */
00174   mfcc->param->samplenum = end;
00175 }
00176 
00193 void
00194 mfcc_shrink(MFCCCalc *mfcc, int p)
00195 {
00196   int t;
00197   int len;
00198 
00199   if (p > 0) {
00200     /* copy data */
00201     for(t=p;t<mfcc->param->samplenum;t++) {
00202       memcpy(mfcc->param->parvec[t-p], mfcc->param->parvec[t], sizeof(VECT) * mfcc->param->veclen);
00203     }
00204     /* shrink original param */
00205     /* just shrink the length */
00206     len = mfcc->param->samplenum - p;
00207     mfcc->param->samplenum = len;
00208     mfcc->param->header.samplenum = len;
00209   }
00210 }
00211 
00261 boolean
00262 detect_end_of_segment(RecogProcess *r, int time)
00263 {
00264   FSBeam *d;
00265   TRELLIS_ATOM *tre;
00266   LOGPROB maxscore = LOG_ZERO;
00267   TRELLIS_ATOM *tremax = NULL;
00268   int count = 0;
00269   boolean detected = FALSE;
00270 #ifdef SPSEGMENT_NAIST
00271   MFCCCalc *mfcc;
00272   WORD_ID wid;
00273   int j;
00274   TOKEN2 *tk;
00275   int startframe;
00276 #endif
00277 
00278   d = &(r->pass1);
00279 
00280 #ifdef SPSEGMENT_NAIST
00281 
00282   if (! d->after_trigger) {
00283     /* we are in the first long pause segment before trigger */
00284 
00285     /* find word end of maximum score from beam status */
00286     for (j = d->n_start; j <= d->n_end; j++) {
00287       tk = &(d->tlist[d->tn][d->tindex[d->tn][j]]);
00288       if (r->wchmm->stend[tk->node] != WORD_INVALID) {
00289         if (maxscore < tk->score) {
00290           maxscore = tk->score;
00291           wid = r->wchmm->stend[tk->node];
00292         }
00293       }
00294     }
00295     if (maxscore == LOG_ZERO) detected = TRUE;
00296     else if (is_sil(wid, r)) detected = TRUE;
00297  
00298     if (detected) {
00299       /***********************/
00300       /* this is noise frame */
00301       /***********************/
00302 
00303       /* reset trigger duration */
00304       d->trigger_duration = 0;
00305       
00306       /* if noise goes more than a certain frame, shrink the noise area
00307          to avoid unlimited memory usage */
00308       if (r->am->mfcc->f > SPSEGMENT_NAIST_AUTOSHRINK_LIMIT) {
00309         d->want_rewind = TRUE;
00310         d->rewind_frame = r->am->mfcc->f - r->config->successive.sp_margin;
00311         d->want_rewind_reprocess = FALSE;
00312         if (debug2_flag) {
00313           jlog("DEBUG: pause exceeded %d, rewind\n", SPSEGMENT_NAIST_AUTOSHRINK_LIMIT);
00314         }
00315         return FALSE;
00316       }
00317 
00318       /* keep going */
00319       d->want_rewind = FALSE;
00320 
00321     } else {
00322       /************************/
00323       /* this is speech frame */
00324       /************************/
00325 
00326       /* increment trigger duration */
00327       d->trigger_duration++;
00328       
00329       /* if not enough duration, not treat as up trigger */
00330       if (d->trigger_duration < r->config->successive.sp_delay) {
00331         /* just continue detection */
00332         return FALSE;
00333       }
00334 
00335       /***************************/
00336       /* found speech up-trigger */
00337       /***************************/
00338       /* set backstep point */
00339       if (r->am->mfcc->f < r->config->successive.sp_margin) {
00340         startframe = 0;
00341       } else {
00342         startframe = r->am->mfcc->f - r->config->successive.sp_margin;
00343       }
00344       if (debug2_flag) {
00345         jlog("DEBUG: speech triggered\n");
00346         jlog("DEBUG: word=[%s] dur=%d\n", r->lm->winfo->woutput[wid], d->trigger_duration);
00347         jlog("DEBUG: backstep behind %d (from %d to %d) frame and start process\n", r->config->successive.sp_margin, r->am->mfcc->f, startframe);
00348       }
00349 
00350       /* if the pause segment was short, keep the context of last segment.
00351          else, reset the context */
00352       if (r->lmtype == LM_PROB) {
00353         if (startframe > 0) {
00354           r->sp_break_last_word = WORD_INVALID;
00355         }
00356       }
00357 
00358       /* reset sp duration */
00359       d->sp_duration = 0;
00360 
00361       /* request the caller to rewind the search to the backstep point and
00362          re-start with normal search */
00363       d->want_rewind = TRUE;
00364       d->rewind_frame = startframe;
00365       d->want_rewind_reprocess = TRUE;
00366       /* this will enter to normal search in the next processing */
00367       d->after_trigger = TRUE;
00368     }
00369     /* tell the caller not to segment */
00370     return FALSE;
00371   }
00372 
00373 #endif /* SPSEGMENT_NAIST */
00374 
00375   /* look for the best trellis word on the given time frame */
00376   for(tre = r->backtrellis->list; tre != NULL && tre->endtime == time; tre = tre->next) {
00377     if (maxscore < tre->backscore) {
00378       maxscore = tre->backscore;
00379       tremax = tre;
00380     }
00381     count++;
00382   }
00383   if (tremax == NULL) { /* no word end: possible in the very beggining of input*/
00384     detected = TRUE;            /* assume it's in the short-pause duration */
00385   } else if (count > 0) {       /* many words found --- check if maximum is sp */
00386     if (is_sil(tremax->wid, r)) {
00387       detected = TRUE;
00388     }
00389   }
00390 
00391 
00392 #ifdef SPSEGMENT_NAIST
00393   /************************************************************************/
00394   /************************************************************************/
00395 
00396   /* detected = TRUE if noise frame, or FALSE if speech frame */
00397 
00398   /* sp区間持続チェック */
00399   /* check sp segment duration */
00400   if (d->first_sparea) {
00401     /* we are in the first sp segment */
00402     if (d->in_sparea && detected) {
00403       /* sp continues */
00404       d->sp_duration++;
00405       /* when sp continues more than -spdur plus -spmargin,
00406          it means that although a speech trigger has been detected
00407          by some reason, no actual speech has been found at first. */
00408       /* in this case we force trigger to end this input */
00409       if (d->sp_duration > r->config->successive.sp_delay + r->config->successive.sp_margin + r->config->successive.sp_frame_duration) {
00410         d->in_sparea = FALSE;
00411         d->first_sparea = FALSE;
00412         if (debug2_flag) {
00413           jlog("DEBUG: no valid speech starts, force trigger at %d\n", r->am->mfcc->f);
00414         }
00415       }
00416     } else if (d->in_sparea && !detected) {
00417       /* found speech frame */
00418       d->in_sparea = FALSE;
00419       d->first_sparea = FALSE;
00420       if (debug2_flag) {
00421         jlog("DEBUG: speech segment start at %d\n", r->am->mfcc->f);
00422       }
00423     }
00424   } else {
00425     /* we are either in speech segment, or trailing sp segment */
00426     if (!d->in_sparea) {
00427       /* we are in speech segment */
00428       if (detected) {
00429         /* detected end of speech segment (begin of sp segment) */
00430         /* 一時的に開始フレームとしてマーク */
00431         /* mark this frame as "temporal" begging of short-pause segment */
00432         d->tmp_sparea_start = time;
00433 #ifdef SP_BREAK_RESUME_WORD_BEGIN
00434         if (r->lmtype == LM_PROB) {
00435           /* sp 区間開始時点の最尤単語を保存 */
00436           /* store the best word in this frame as resuming word */
00437           d->tmp_sp_break_last_word = tremax ? tremax->wid : WORD_INVALID;
00438         }
00439 #endif
00440         d->in_sparea = TRUE;
00441         d->sp_duration = 1;
00442       } else {
00443         /* speech continues */
00444         /* keep recognizing */
00445       }
00446     } else {
00447       /* we are in trailing sp segment */
00448       if (detected) {
00449         /* short pause frame continues */
00450         d->sp_duration++;
00451         /* keep word as the "beggining" of next sp segment */
00452         if (r->lmtype == LM_PROB) {
00453 #ifdef SP_BREAK_RESUME_WORD_BEGIN
00454           /* if this segment has triggered by (tremax == NULL) (in case the first
00455              several frame of input), the sp word (to be used as resuming
00456              word in the next segment) is not yet set. it will be detected here */
00457           if (d->tmp_sp_break_last_word == WORD_INVALID) {
00458             if (tremax != NULL) d->tmp_sp_break_last_word = tremax->wid;
00459           }
00460 #else
00461           /* resume word at the "end" of sp segment */
00462           /* simply update the best sp word */
00463           if (tremax != NULL) d->last_tre_word = tremax->wid;
00464 #endif
00465         }
00466 
00467         if (d->sp_duration >= r->config->successive.sp_frame_duration) {
00468           /* silence over, segment the recognition here */
00469           /* store begging frame of the segment */
00470           //d->sparea_start = d->tmp_sparea_start;
00471           r->am->mfcc->sparea_start = time - r->config->successive.sp_frame_duration;
00472           if (r->lmtype == LM_PROB) {
00473 #ifdef SP_BREAK_RESUME_WORD_BEGIN
00474             /* resume word = most likely sp word on beginning frame of the segment */
00475             r->sp_break_last_word = d->tmp_sp_break_last_word;
00476 #else
00477             /* resume word = most likely sp word on end frame of the segment */
00478             r->sp_break_last_word = d->last_tre_word;
00479 #endif
00480           }
00481 
00482           if (debug2_flag) {
00483             jlog("DEBUG: trailing silence end, end this segment at %d\n", r->am->mfcc->f);
00484           }
00485           
00486           d->after_trigger = FALSE;
00487           d->trigger_duration = 0;
00488           d->want_rewind = FALSE;
00489 
00490           /*** segment: [sparea_start - time-1] ***/
00491           return(TRUE);
00492         }
00493         /* else, keep recognition */
00494       } else {
00495         /* speech re-triggered */
00496         /* keep recognition */
00497         d->in_sparea = FALSE;
00498       }
00499     }
00500   }
00501 
00502   d->want_rewind = FALSE;
00503 
00504 
00505 #else  /* ~SPSEGMENT_NAIST */
00506   /************************************************************************/
00507   /************************************************************************/
00508 
00509   /* sp区間持続チェック */
00510   /* check sp segment duration */
00511   if (d->in_sparea && detected) {       /* we are already in sp segment and sp continues */
00512     d->sp_duration++;           /* increment count */
00513 #ifdef SP_BREAK_RESUME_WORD_BEGIN
00514     /* resume word at the "beggining" of sp segment */
00515     /* if this segment has triggered by (tremax == NULL) (in case the first
00516        several frame of input), the sp word (to be used as resuming
00517        word in the next segment) is not yet set. it will be detected here */
00518     if (d->tmp_sp_break_last_word == WORD_INVALID) {
00519       if (tremax != NULL) d->tmp_sp_break_last_word = tremax->wid;
00520     }
00521 #else
00522     /* resume word at the "end" of sp segment */
00523     /* simply update the best sp word */
00524     if (tremax != NULL) d->last_tre_word = tremax->wid;
00525 #endif
00526   }
00527 
00528   /* sp区間開始チェック */
00529   /* check if sp segment begins at this frame */
00530   else if (!d->in_sparea && detected) {
00531     /* 一時的に開始フレームとしてマーク */
00532     /* mark this frame as "temporal" begging of short-pause segment */
00533     d->tmp_sparea_start = time;
00534 #ifdef SP_BREAK_RESUME_WORD_BEGIN
00535     /* sp 区間開始時点の最尤単語を保存 */
00536     /* store the best word in this frame as resuming word */
00537     d->tmp_sp_break_last_word = tremax ? tremax->wid : WORD_INVALID;
00538 #endif
00539     d->in_sparea = TRUE;                /* yes, we are in sp segment */
00540     d->sp_duration = 1;         /* initialize duration count */
00541 #ifdef SP_BREAK_DEBUG
00542     jlog("DEBUG: sp start %d\n", time);
00543 #endif /* SP_BREAK_DEBUG */
00544   }
00545   
00546   /* sp 区間終了チェック */
00547   /* check if sp segment ends at this frame */
00548   else if (d->in_sparea && !detected) {
00549     /* (time-1) is end frame of pause segment */
00550     d->in_sparea = FALSE;               /* we are not in sp segment */
00551 #ifdef SP_BREAK_DEBUG
00552     jlog("DEBUG: sp end %d\n", time);
00553 #endif /* SP_BREAK_DEBUG */
00554     /* sp 区間長チェック */
00555     /* check length of the duration*/
00556     if (d->sp_duration < r->config->successive.sp_frame_duration) {
00557       /* 短すぎる: 第1パスを中断せず続行 */
00558       /* too short segment: not break, continue 1st pass */
00559 #ifdef SP_BREAK_DEBUG
00560       jlog("DEBUG: too short (%d<%d), ignored\n", d->sp_duration, r->config->successive.sp_frame_duration);
00561 #endif /* SP_BREAK_DEBUG */
00562     } else if (d->first_sparea) {
00563       /* 最初のsp区間は silB にあたるので,第1パスを中断せず続行 */
00564       /* do not break at first sp segment: they are silB */
00565       d->first_sparea = FALSE;
00566 #ifdef SP_BREAK_DEBUG
00567       jlog("DEBUG: first silence, ignored\n");
00568 #endif /* SP_BREAK_DEBUG */
00569     } else {
00570       /* 区間終了確定, 第1パスを中断して第2パスへ */
00571       /* break 1st pass */
00572 #ifdef SP_BREAK_DEBUG
00573       jlog("DEBUG: >> segment [%d..%d]\n", r->am->mfcc->sparea_start, time-1);
00574 #endif /* SP_BREAK_DEBUG */
00575       /* store begging frame of the segment */
00576       r->am->mfcc->sparea_start = d->tmp_sparea_start;
00577 #ifdef SP_BREAK_RESUME_WORD_BEGIN
00578       /* resume word = most likely sp word on beginning frame of the segment */
00579       r->sp_break_last_word = d->tmp_sp_break_last_word;
00580 #else
00581       /* resume word = most likely sp word on end frame of the segment */
00582       r->sp_break_last_word = d->last_tre_word;
00583 #endif
00584 
00585       /*** segment: [sparea_start - time-1] ***/
00586       return(TRUE);
00587     }
00588   }
00589 
00590 
00591 #endif  /* ~SPSEGMENT_NAIST */
00592 
00593     
00594 #ifdef SP_BREAK_EVAL
00595   jlog("DEBUG: [%d %d %d]\n", time, count, (detected) ? 50 : 0);
00596 #endif
00597   return (FALSE);
00598 }
00599 
00600 /*******************************************************************/
00601 /* 第1パスセグメント終了処理 (ショートポーズセグメンテーション用) */
00602 /* end of 1st pass for a segment (for short pause segmentation)    */
00603 /*******************************************************************/
00631 void
00632 finalize_segment(Recog *recog)
00633 {
00634   int spstart;
00635   RecogProcess *r;
00636   MFCCCalc *mfcc;
00637   boolean ok_p;
00638 
00639   /* トレリス始終端における最尤単語を第2パスの始終端単語として格納 */
00640   /* fix initial/last word hypothesis of the next 2nd pass to the best
00641      word hypothesis at the first/last frame in backtrellis*/
00642   for(r=recog->process_list;r;r=r->next) {
00643     if (!r->live) continue;
00644     if (r->lmtype == LM_PROB) {
00645       set_terminal_words(r);
00646     }
00647   }
00648 
00649   /* パラメータを, 今第1パスが終了したセグメント区間と残りの区間に分割する. 
00650      ただし接続部のsp区間部分(sparea_start..len-1)は「のりしろ」として両方に
00651      コピーする */
00652   /* Divide input parameter into two: the last segment and the rest.
00653      The short-pause area (sparea_start..len-1) is considered as "tab",
00654      copied in both parameters
00655    */
00656   /* param[sparea_start..framelen] -> rest_param
00657      param[0..len-1] -> param
00658      [sparea_start...len-1] overlapped
00659   */
00660 
00661   ok_p = FALSE;
00662   for (mfcc = recog->mfcclist; mfcc; mfcc = mfcc->next) {
00663     if (mfcc->segmented) {
00664       spstart = mfcc->sparea_start;
00665       ok_p = TRUE;
00666       break;
00667     }
00668   }
00669 
00670   if (ok_p) {
00671     /* the input was segmented in an instance */
00672     /* shrink all param the len and store restart parameters in rest_param */
00673     /* for each mfcc */
00674     if (verbose_flag) jlog("STAT: segmented: next decoding will restart from %d\n", spstart);
00675 
00676     for (mfcc = recog->mfcclist; mfcc; mfcc = mfcc->next) {
00677       if (verbose_flag) jlog("STAT: MFCC%02d: segmented: processed length=%d\n", mfcc->id, mfcc->last_time);
00678 
00679       /* copy the rest to mfcc->rest_param and shrink mfcc->param */
00680       mfcc_copy_to_rest_and_shrink(mfcc, spstart, mfcc->last_time);
00681     }
00682 
00683     /* reset last_word info */
00684     for(r=recog->process_list;r;r=r->next) {
00685       if (!r->live) continue;
00686       r->sp_break_last_nword_allow_override = TRUE;
00687     }
00688     
00689   } else {
00690     
00691     /* last segment is on end of input: no rest parameter */
00692     for (mfcc = recog->mfcclist; mfcc; mfcc = mfcc->next) {
00693       mfcc->rest_param = NULL;
00694     }
00695     /* reset last_word info */
00696     for(r=recog->process_list;r;r=r->next) {
00697       if (!r->live) continue;
00698       r->sp_break_2_begin_word = WORD_INVALID;
00699       r->sp_break_last_word = WORD_INVALID;
00700       r->sp_break_last_nword = WORD_INVALID;
00701       r->sp_break_last_nword_allow_override = FALSE;
00702     }
00703   }
00704 }
00705 
00706 #ifdef BACKEND_VAD
00707 
00723 void
00724 spsegment_init(Recog *recog)
00725 {
00726   RecogProcess *p;
00727   /* at first time, recognition does not start yet */
00728 #ifdef SPSEGMENT_NAIST
00729   for(p=recog->process_list;p;p=p->next) {
00730     p->pass1.after_trigger = FALSE;
00731     p->pass1.trigger_duration = 0;
00732   }
00733 #endif
00734 #ifdef GMM_VAD
00735   if (recog->gmm) {
00736     recog->gc->after_trigger = FALSE;
00737     recog->gc->duration = 0;
00738   }
00739 #endif
00740   recog->triggered = FALSE;
00741 }
00742 
00767 boolean
00768 spsegment_trigger_sync(Recog *recog)
00769 {
00770   RecogProcess *p;
00771   boolean ok_p;
00772 
00773   ok_p = FALSE;
00774   if (recog->jconf->decodeopt.segment) {
00775 #ifdef SPSEGMENT_NAIST
00776     for(p = recog->process_list; p; p = p->next) {
00777       if (!p->live) continue;
00778       if (p->pass1.after_trigger) {
00779         ok_p = TRUE;
00780         break;
00781       }
00782     }
00783 #endif
00784 #ifdef GMM_VAD
00785     if (recog->gmm) {
00786       if (recog->gc->after_trigger) {
00787         ok_p = TRUE;
00788       }
00789     }
00790 #endif
00791   }
00792   if (ok_p) {
00793     /* up trigger detected */
00794 #ifdef SPSEGMENT_NAIST
00795     for(p = recog->process_list; p; p = p->next) {
00796       if (!p->live) continue;
00797       p->pass1.after_trigger = TRUE;
00798     }
00799 #endif
00800 #ifdef GMM_VAD
00801     if (recog->gmm) {
00802       recog->gc->after_trigger = TRUE;
00803     }
00804 #endif
00805   }
00806   
00807   return ok_p;
00808 }
00809 
00810 #endif /* BACKEND_VAD */
00811 
00836 boolean
00837 spsegment_need_restart(Recog *recog, int *rf_ret, boolean *repro_ret)
00838 {
00839 #ifdef SPSEGMENT_NAIST
00840   RecogProcess *p;
00841 #endif
00842   boolean ok_p;
00843   int rewind_frame = 0;
00844   boolean reprocess = FALSE;
00845 
00846   ok_p = FALSE;
00847   if (recog->jconf->decodeopt.segment) {
00848 #ifdef SPSEGMENT_NAIST
00849     /* check for rewind request from each process */
00850     for(p = recog->process_list; p; p = p->next) {
00851       if (!p->live) continue;
00852       if (p->pass1.want_rewind) {
00853         p->pass1.want_rewind = FALSE;
00854         rewind_frame = p->pass1.rewind_frame;
00855         reprocess = p->pass1.want_rewind_reprocess;
00856         ok_p = TRUE;
00857         break;
00858       }
00859     }
00860 #endif /* SPSEGMENT_NAIST */
00861 #ifdef GMM_VAD
00862     if (recog->gmm) {
00863       if (recog->gc->want_rewind) {
00864         recog->gc->want_rewind = FALSE;
00865 #ifdef SPSEGMENT_NAIST
00866         /* set to earlier one */
00867         if (rewind_frame > recog->gc->rewind_frame) rewind_frame = recog->gc->rewind_frame;
00868 #else
00869         rewind_frame = recog->gc->rewind_frame;
00870 #endif
00871         reprocess = recog->gc->want_rewind_reprocess;
00872         ok_p = TRUE;
00873       }
00874     }
00875 #endif
00876     *rf_ret = rewind_frame;
00877     *repro_ret = reprocess;
00878   }
00879 
00880   return(ok_p);
00881 }
00882 
00909 void
00910 spsegment_restart_mfccs(Recog *recog, int rewind_frame, boolean reprocess)
00911 {
00912   MFCCCalc *mfcc;
00913 
00914   for (mfcc = recog->mfcclist; mfcc; mfcc = mfcc->next) {
00915     if (!mfcc->valid) continue;
00916     /* set last segmented time */
00917     mfcc->last_time = mfcc->f - 1;
00918     /* reset frame pointers */
00919     if (reprocess) {
00920       /* set all mfcc to initial point for re-process the whole frames */
00921       mfcc->f = -1;
00922     } else {
00923       /* just bring back to the new last point after shrink */
00924       mfcc->f -= rewind_frame;
00925     }
00926     /* shrink the current mfcc */
00927     mfcc_shrink(mfcc, rewind_frame);
00928   }
00929 }
00930 
00931 /* end of file */