/**
 * @file   julius-simple.c
 * 
 * <EN>
 * @brief  Sample code for JuliusLib
 *
 * This is a simple code to link JuliusLib and do recognition.
 * 
 * This program will output recognition result to stdout according
 * to the given jconf setting.
 * </EN>
 * 
 * <JA>
 * @brief  JuliusLib用サンプルコード
 *
 * このコードは JuliusLib をリンクして簡単な認識器を構築します．
 *
 * このプログラムは音声入力に対して与えられた jconf 設定のもとで
 * 認識を行い，結果を標準出力に出力します．
 * 
 * </JA>
 * 
 * @author Akinobu Lee
 * @date   Tue Dec 11 14:40:04 2007
 * 
 * $Revision: 1.4 $
 * 
 */

/* include top Julius library header */
#include <julius/juliuslib.h>

/** 
 * Callback to be called when start waiting speech input. 
 * 
 */
static void
status_recready(Recog *recog, void *dummy) {
    if (recog->jconf->input.speech_input == SP_MIC || recog->jconf->input.speech_input == SP_NETAUDIO) {
        fprintf(stderr, "<<< please speak >>>");
    }
}

/** 
 * Callback to be called when speech input is triggered.
 * 
 */
static void
status_recstart(Recog *recog, void *dummy) {
    if (recog->jconf->input.speech_input == SP_MIC || recog->jconf->input.speech_input == SP_NETAUDIO) {
        fprintf(stderr, "\r                    \r");
    }
}

/** 
 * Sub function to output phoneme sequence.
 * 
 */
static void
put_hypo_phoneme(WORD_ID *seq, int n, WORD_INFO *winfo) {
    int i, j;
    WORD_ID w;
    static char buf[MAX_HMMNAME_LEN];

    if (seq != NULL) {
        for (i = 0; i < n; i++) {
            if (i > 0) printf(" |");
            w = seq[i];
            for (j = 0; j < winfo->wlen[w]; j++) {
                center_name(winfo->wseq[w][j]->name, buf);
                printf(" %s", buf);
            }
        }
    }
    printf("\n");
}

/** 
 * Callback to output final recognition result.
 * This function will be called just after recognition of an input ends
 * 
 */
static void
output_result(Recog *recog, void *dummy) {
    int i, j;
    int len;
    WORD_INFO *winfo;
    WORD_ID *seq;
    int seqnum;
    int n;
    Sentence *s;
    RecogProcess *r;
    HMM_Logical *p;
    SentenceAlign *align;

    /* all recognition results are stored at each recognition process
       instance */
    for (r = recog->process_list; r; r = r->next) {

        /* skip the process if the process is not alive */
        if (!r->live) continue;

        /* result are in r->result.  See recog.h for details */

        /* check result status */
        if (r->result.status < 0) { /* no results obtained */
            /* outout message according to the status code */
            switch (r->result.status) {
                case J_RESULT_STATUS_REJECT_POWER:
                    printf("<input rejected by power>\n");
                    break;
                case J_RESULT_STATUS_TERMINATE:
                    printf("<input teminated by request>\n");
                    break;
                case J_RESULT_STATUS_ONLY_SILENCE:
                    printf("<input rejected by decoder (silence input result)>\n");
                    break;
                case J_RESULT_STATUS_REJECT_GMM:
                    printf("<input rejected by GMM>\n");
                    break;
                case J_RESULT_STATUS_REJECT_SHORT:
                    printf("<input rejected by short input>\n");
                    break;
                case J_RESULT_STATUS_REJECT_LONG:
                    printf("<input rejected by long input>\n");
                    break;
                case J_RESULT_STATUS_FAIL:
                    printf("<search failed>\n");
                    break;
            }
            /* continue to next process instance */
            continue;
        }

        /* output results for all the obtained sentences */
        winfo = r->lm->winfo;

        for (n = 0; n < r->result.sentnum; n++) { /* for all sentences */

            s = &(r->result.sent[n]);
            seq = s->word;
            seqnum = s->word_num;

            /* output word sequence like Julius */
            printf("sentence%d:", n + 1);
            for (i = 0; i < seqnum; i++) printf(" %s", winfo->woutput[seq[i]]);
            printf("\n");
            /* LM entry sequence */
            printf("wseq%d:", n + 1);
            for (i = 0; i < seqnum; i++) printf(" %s", winfo->wname[seq[i]]);
            printf("\n");
            /* phoneme sequence */
            printf("phseq%d:", n + 1);
            put_hypo_phoneme(seq, seqnum, winfo);
            printf("\n");
            /* confidence scores */
            printf("cmscore%d:", n + 1);
            for (i = 0; i < seqnum; i++) printf(" %5.3f", s->confidence[i]);
            printf("\n");
            /* AM and LM scores */
            printf("score%d: %f", n + 1, s->score);
            if (r->lmtype == LM_PROB) { /* if this process uses N-gram */
                printf(" (AM: %f  LM: %f)", s->score_am, s->score_lm);
            }
            printf("\n");
            if (r->lmtype == LM_DFA) { /* if this process uses DFA grammar */
                /* output which grammar the hypothesis belongs to
                   when using multiple grammars */
                if (multigram_get_all_num(r->lm) > 1) {
                    printf("grammar%d: %d\n", n + 1, s->gram_id);
                }
            }

            /* output alignment result if exist */
            for (align = s->align; align; align = align->next) {
                printf("=== begin forced alignment ===\n");
                switch (align->unittype) {
                    case PER_WORD:
                        printf("-- word alignment --\n");
                        break;
                    case PER_PHONEME:
                        printf("-- phoneme alignment --\n");
                        break;
                    case PER_STATE:
                        printf("-- state alignment --\n");
                        break;
                }
                printf(" id: from  to    n_score    unit\n");
                printf(" ----------------------------------------\n");
                for (i = 0; i < align->num; i++) {
                    printf("[%4d %4d]  %f  ", align->begin_frame[i], align->end_frame[i], align->avgscore[i]);
                    switch (align->unittype) {
                        case PER_WORD:
                            printf("%s\t[%s]\n", winfo->wname[align->w[i]], winfo->woutput[align->w[i]]);
                            break;
                        case PER_PHONEME:
                            p = align->ph[i];
                            if (p->is_pseudo) {
                                printf("{%s}\n", p->name);
                            } else if (strmatch(p->name, p->body.defined->name)) {
                                printf("%s\n", p->name);
                            } else {
                                printf("%s[%s]\n", p->name, p->body.defined->name);
                            }
                            break;
                        case PER_STATE:
                            p = align->ph[i];
                            if (p->is_pseudo) {
                                printf("{%s}", p->name);
                            } else if (strmatch(p->name, p->body.defined->name)) {
                                printf("%s", p->name);
                            } else {
                                printf("%s[%s]", p->name, p->body.defined->name);
                            }
                            if (r->am->hmminfo->multipath) {
                                if (align->is_iwsp[i]) {
                                    printf(" #%d (sp)\n", align->loc[i]);
                                } else {
                                    printf(" #%d\n", align->loc[i]);
                                }
                            } else {
                                printf(" #%d\n", align->loc[i]);
                            }
                            break;
                    }
                }

                printf("re-computed AM score: %f\n", align->allscore);

                printf("=== end forced alignment ===\n");
            }
        }
    }

    /* flush output buffer */
    fflush(stdout);
}


/* --------------------- adin check callback --------------- */

/** 
 * <JA>
 * @brief  音声入力中に定期的に実行されるコールバック. 
 *
 * この関数は，adin_go() にて音声入力待ち，あるいは音声認識中に
 * 定期的に繰り返し呼び出される関数である. ユーザ定義のコールバック
 * (CALLBACK_POLL) の呼び出し，および中断判定を行う. 
 *
 * @param recog [in] エンジンインスタンス
 * 
 * @return 通常時 0, 即時中断を要求時 -2, 認識中止の要求時は -1 を返す. 
 * </JA>
 * <EN>
 * @brief  callback function periodically called while input.
 *
 * This function will be called periodically from adin_go() while
 * waiting input or processing recognition.  It will call user-defined
 * callback registered in CALLBACK_POLL,  check for the process
 * status and issue recognition termination request.
 *
 * @param recog [in] engine instance
 * 
 * @return 0 normally, -2 for immediate termination, and -1 if requesting
 * recognition stop.
 * 
 * </EN>
 */
static int
callback_check_in_adin(Recog *recog) {
    /* module: check command and terminate recording when requested */
    callback_exec(CALLBACK_POLL, recog);
    /* With audio input via adinnet, TERMINATE command will issue terminate
       command to the adinnet client.  The client then stops recording
       immediately and return end-of-segment ack.  Then it will cause this
       process to stop recognition as normal.  So we need not to
       perform immediate termination at this callback, but just ignore the
       results in the main.c.  */
#if 1
    if (recog->process_want_terminate) { /* TERMINATE ... force termination */
        return (-2);
    }
    if (recog->process_want_reload) {
        return (-1);
    }
#else
    if (recog->process_want_terminate /* TERMINATE ... force termination */
            && recog->jconf->input.speech_input != SP_ADINNET) {
        return (-2);
    }
    if (recog->process_want_reload) {
        return (-1);
    }
#endif
    return (0);
}

/** 
 * <EN>
 * Recognition error handling.
 * </EN>
 * <JA>
 * エラーによる認識終了時の処理. 
 * </JA>
 * 
 * @param recog [in] engine instance
 * @param status [in] error status to be set
 * 
 */
static void
result_error(Recog *recog, int status) {
    MFCCCalc *mfcc;
    RecogProcess *r;
    boolean ok_p;

    for (r = recog->process_list; r; r = r->next) r->result.status = status;

    ok_p = FALSE;
    for (mfcc = recog->mfcclist; mfcc; mfcc = mfcc->next) {
        if (mfcc->f > 0) {
            ok_p = TRUE;
            break;
        }
    }
    if (ok_p) { /* had some input */
        /* output as rejected */
        callback_exec(CALLBACK_RESULT, recog);
#ifdef ENABLE_PLUGIN
        plugin_exec_process_result(recog);
#endif
    }
}

static boolean
_core__need_on_the_fly(Jconf *jconf) {
    switch (jconf->input.type) {
        case INPUT_VECTOR:
            switch (jconf->input.speech_input) {
                case SP_MFCFILE:
                    return FALSE;
                    break;
                case SP_MFCMODULE:
                    return TRUE;
                    break;
            }
            break;
        case INPUT_WAVEFORM:
            if (jconf->decodeopt.realtime_flag) {
                return TRUE;
            } else {
                return FALSE;
            }
            break;
    }
    return FALSE;
}

static void
_core__update_status(Recog *recog) {
    boolean ok_p;
    RecogProcess *r;

    for (r = recog->process_list; r; r = r->next) {
        if (r->active > 0) {
            r->live = TRUE;
            jlog("STAT: SR%02d %s now active\n", r->config->id, r->config->name);
        } else if (r->active < 0) {
            r->live = FALSE;
            jlog("STAT: SR%02d %s now inactive\n", r->config->id, r->config->name);
        }
        r->active = 0;
    }
    if (debug2_flag) {
        for (r = recog->process_list; r; r = r->next) {
            jlog("DEBUG: %s: SR%02d %s\n", r->live ? "live" : "dead", r->config->id, r->config->name);
        }
    }
    /* check if any process is live */
    if (recog->process_active) {
        ok_p = FALSE;
        for (r = recog->process_list; r; r = r->next) {
            if (r->live) ok_p = TRUE;
        }
        if (!ok_p) { /* no process is alive */
            /* make whole process as inactive */
            jlog("STAT: all recog process inactive, pause engine now\n");
            j_request_pause(recog);
        }
    }

    /* Check whether process status was changed while in the last run */
    if (recog->process_online != recog->process_active) {
        recog->process_online = recog->process_active;
        if (recog->process_online) callback_exec(CALLBACK_EVENT_PROCESS_ONLINE, recog);
        else callback_exec(CALLBACK_EVENT_PROCESS_OFFLINE, recog);
    }
    /* execute poll callback */
    if (recog->process_active) {
        callback_exec(CALLBACK_POLL, recog);
    }
    /* reset reload flag here */
    j_reset_reload(recog);
}

static void
_core__update_grammar(Recog *recog) {
    RecogProcess *r;
    PROCESS_LM *lm;

    for (lm = recog->lmlist; lm; lm = lm->next) {
        if (lm->lmtype == LM_DFA) {
            multigram_update(lm); /* some modification occured if return TRUE*/
        }
    }
    for (r = recog->process_list; r; r = r->next) {
        if (!r->live) continue;
        if (r->lmtype == LM_DFA && r->lm->global_modified) {
            multigram_build(r);
        }
    }
    for (lm = recog->lmlist; lm; lm = lm->next) {
        if (lm->lmtype == LM_DFA) lm->global_modified = FALSE;
    }
}

static int
_core__firstpass_realtime(Recog *recog) {
    int ret;
    /********************************************/
    /* REALTIME ON-THE-FLY DECODING OF 1ST-PASS */
    /********************************************/
    /* end of this input will be determined by either end of stream
       (in case of file input), or silence detection by adin_go(), or
       'TERMINATE' command from module (if module mode) */
    /* prepare work area for on-the-fly processing */
    fprintf(stderr, "Prepare\n");
    if (RealTimePipeLinePrepare(recog) == FALSE) {
        jlog("ERROR: failed to prepare for on-the-fly 1st pass decoding\n");
        return (-1);
    }
    /* get speech and process it on real-time */
    //ret = adin_go(RealTimePipeLine, callback_check_in_adin, recog);
    {
        char* filename = "sample.le";
        SP16 buf[4096];
        size_t nRead = 0;
        FILE *fp = fopen(filename, "rb");
        while ((nRead = fread(buf, sizeof (SP16), 4096, fp)) > 0) {
            fprintf(stderr, "%d, ", (int) nRead);
            RealTimePipeLine(buf, nRead, recog);
        }
        ret = 0;
    }

    fprintf(stderr, "adin_go = %d\n", ret);

    /* last procedure of 1st-pass */
    if (RealTimeParam(recog) == FALSE) {
        jlog("ERROR: fatal error occured, program terminates now\n");
        return -1;
    }

    /* execute callback for 1st pass result */
    /* result.status <0 must be skipped inside callback */
    callback_exec(CALLBACK_RESULT_PASS1, recog);
#ifdef WORD_GRAPH
    /* result.wg1 == NULL should be skipped inside callback */
    callback_exec(CALLBACK_RESULT_PASS1_GRAPH, recog);
#endif
    /* execute callback at end of pass1 */
    callback_exec(CALLBACK_EVENT_PASS1_END, recog);
    /* output frame length */
    callback_exec(CALLBACK_STATUS_PARAM, recog);
    /* if terminate signal has been received, discard this input */
    //            if (recog->process_want_terminate) {
    //                result_error(recog, J_RESULT_STATUS_TERMINATE);
    //                goto end_recog;
    //            }

    /* END OF ON-THE-FLY INPUT AND DECODING OF 1ST PASS */
    return ret;
}

static int
_core__firstpass_batch(Recog *recog) {
    PROCESS_AM *am;
    MFCCCalc *mfcc;
    Jconf *jconf = recog->jconf;

    int ret;
    float seclen, mseclen;
    /*************************/
    /* buffered speech input */
    /*************************/
    if (!recog->process_segment) { /* no segment left */

        /****************************************/
        /* store raw speech samples to speech[] */
        /****************************************/
        recog->speechlen = 0;
        for (mfcc = recog->mfcclist; mfcc; mfcc = mfcc->next) {
            param_init_content(mfcc->param);
        }
        /* tell module to start recording */
        /* the "adin_cut_callback_store_buffer" simply stores
           the input speech to a buffer "speech[]" */
        /* end of this input will be determined by either end of stream
           (in case of file input), or silence detection by adin_go(), or
           'TERMINATE' command from module (if module mode) */
        ret = adin_go(adin_cut_callback_store_buffer, callback_check_in_adin, recog);
#if 0
        if (ret < 0) { /* error end in adin_go */
            if (ret == -2 || recog->process_want_terminate) {
                /* terminated by module */
                /* output fail */
                result_error(recog, J_RESULT_STATUS_TERMINATE);
                goto end_recog;
            }
            jlog("ERROR: an error occured while recording input\n");
            return -1;
        }
#endif
        /* output recorded length */
        seclen = (float) recog->speechlen / (float) jconf->input.sfreq;
        jlog("STAT: %d samples (%.2f sec.)\n", recog->speechlen, seclen);

#if 0
        /* -rejectshort 指定時, 入力が指定時間以下であれば
           ここで入力を棄却する */
        /* when using "-rejectshort", and input was shorter than
           specified, reject the input here */
        if (jconf->reject.rejectshortlen > 0) {
            if (seclen * 1000.0 < jconf->reject.rejectshortlen) {
                result_error(recog, J_RESULT_STATUS_REJECT_SHORT);
                goto end_recog;
            }
        }
        /* when using "-rejectlong", and input was longer than specified,
           terminate the input here */
        if (recog->jconf->reject.rejectlonglen >= 0) {
            if (seclen * 1000.0 >= recog->jconf->reject.rejectlonglen) {
                result_error(recog, J_RESULT_STATUS_REJECT_LONG);
                goto end_recog;
            }
        }
#endif

        /**********************************************/
        /* acoustic analysis and encoding of speech[] */
        /**********************************************/
        jlog("STAT: ### speech analysis (waveform -> MFCC)\n");
        /* CMN will be computed for the whole buffered input */
        if (wav2mfcc(recog->speech, recog->speechlen, recog) == FALSE) {
            /* error end, end stream */
            ret = -1;
            /* tell failure */
            result_error(recog, J_RESULT_STATUS_FAIL);
            //                    goto end_recog;
        }

        //                /* if terminate signal has been received, cancel this input */
        //                if (recog->process_want_terminate) {
        //                    result_error(recog, J_RESULT_STATUS_TERMINATE);
        //                    goto end_recog;
        //                }

        /* output frame length */
        callback_exec(CALLBACK_STATUS_PARAM, recog);
    }

#ifdef ENABLE_PLUGIN
    /* call post-process plugin if exist */
    plugin_exec_vector_postprocess_all(recog->mfcclist->param);
#endif

    /******************************************************/
    /* 1st-pass --- backward search to compute heuristics */
    /******************************************************/
    if (!jconf->decodeopt.realtime_flag) {
        /* prepare for outprob cache for each HMM state and time frame */
        /* assume all MFCCCalc has params of the same sample num */
        for (am = recog->amlist; am; am = am->next) {
            outprob_prepare(&(am->hmmwrk), am->mfcc->param->samplenum);
        }
    }

    //            /* if terminate signal has been received, cancel this input */
    //            if (recog->process_want_terminate) {
    //                result_error(recog, J_RESULT_STATUS_TERMINATE);
    //                goto end_recog;
    //            }

    /* execute computation of left-to-right backtrellis */
    if (get_back_trellis(recog) == FALSE) {
        jlog("ERROR: fatal error occured, program terminates now\n");
        return -1;
    }
#ifdef BACKEND_VAD
    /* if not triggered, skip this segment */
    if (recog->jconf->decodeopt.segment && !recog->triggered) {
        goto end_recog;
    }
#endif

    /* execute callback for 1st pass result */
    /* result.status <0 must be skipped inside callback */
    callback_exec(CALLBACK_RESULT_PASS1, recog);
#ifdef WORD_GRAPH
    /* result.wg1 == NULL should be skipped inside callback */
    callback_exec(CALLBACK_RESULT_PASS1_GRAPH, recog);
#endif

    /* execute callback at end of pass1 */
    if (recog->triggered) {
        callback_exec(CALLBACK_EVENT_PASS1_END, recog);
    }

    /* END OF BUFFERED 1ST PASS */
}

static int
j_recognize_stream_core(Recog *recog) {
    Jconf *jconf;
    int ret;
    float seclen, mseclen;
    RecogProcess *r;
    MFCCCalc *mfcc;
    PROCESS_AM *am;
    PROCESS_LM *lm;
    boolean ok_p;
    boolean process_segment_last;
    boolean on_the_fly;
    boolean pass2_p;

    jconf = recog->jconf;

    /* determine whether on-the-fly decoding should be done */
    on_the_fly = _core__need_on_the_fly(jconf);

    if (jconf->input.type == INPUT_WAVEFORM || jconf->input.speech_input == SP_MFCMODULE) {
        for (mfcc = recog->mfcclist; mfcc; mfcc = mfcc->next) {
            //            param_init_content(mfcc->param);
        }
    }

    /* if no process instance exist, start with terminated */
    if (recog->process_list == NULL) {
        jlog("STAT: no recog process, engine inactive\n");
        j_request_pause(recog);
    }

    /* update initial recognition process status */
    for (r = recog->process_list; r; r = r->next) {
        if (r->active > 0) {
            r->live = TRUE;
        } else if (r->active < 0) {
            r->live = FALSE;
        }
        r->active = 0;
    }

    /******************************************************************/
    /* do recognition for each incoming segment from the input stream */
    /******************************************************************/
    while (1) {

start_recog:

        /*************************************/
        /* Update recognition process status */
        /*************************************/
        //_core__update_status(recog);

        if (!recog->process_active) {
            /* now sleeping, return */
            /* in the next call, we will resume from here */
            return 1;
        }
        /* update process status */
        if (recog->process_online != recog->process_active) {
            recog->process_online = recog->process_active;
            //            if (recog->process_online) callback_exec(CALLBACK_EVENT_PROCESS_ONLINE, recog);
            //            else callback_exec(CALLBACK_EVENT_PROCESS_OFFLINE, recog);
        }

        /*********************************************************/
        /* check for grammar to change, and rebuild if necessary */
        /*********************************************************/
        //_core__update_grammar(recog);

        ok_p = FALSE;
        for (r = recog->process_list; r; r = r->next) {
            if (!r->live) continue;
            if (r->lmtype == LM_DFA) {
                if (r->lm->winfo == NULL ||
                        (r->lmvar == LM_DFA_GRAMMAR && r->lm->dfa == NULL)) {
                    /* make this instance inactive */
                    r->active = -1;
                    ok_p = TRUE;
                }
            }
        }
        fprintf(stderr, "??\n");
        if (ok_p) { /* at least one instance has no grammar */
            goto start_recog;
        }

        fprintf(stderr, "onthefly = %d\n", on_the_fly);

        /******************/
        /* start 1st pass */
        /******************/
        if (on_the_fly) {
            ret = _core__firstpass_realtime(recog);

        } else {
            /******************/
            /* buffered input */
            /******************/
            ret = _core__firstpass_batch(recog);
        }

        /**********************************/
        /* end processing of the 1st-pass */
        /**********************************/
        /* on-the-fly 1st pass processing will join here */

        /* -rejectshort 指定時, 入力が指定時間以下であれば探索失敗として */
        /* 第２パスを実行せずにここで終了する */
        /* when using "-rejectshort", and input was shorter than the specified
           length, terminate search here and output recognition failure */
        if (jconf->reject.rejectshortlen > 0) {
            mseclen = (float) recog->mfcclist->param->samplenum * (float) jconf->input.period * (float) jconf->input.frameshift / 10000.0;
            if (mseclen < jconf->reject.rejectshortlen) {
                result_error(recog, J_RESULT_STATUS_REJECT_SHORT);
                goto end_recog;
            }
        }
        if (jconf->reject.rejectlonglen >= 0) {
            mseclen = (float) recog->mfcclist->param->samplenum * (float) jconf->input.period * (float) jconf->input.frameshift / 10000.0;
            if (mseclen >= jconf->reject.rejectlonglen) {
                result_error(recog, J_RESULT_STATUS_REJECT_LONG);
                goto end_recog;
            }
        }
#ifdef POWER_REJECT
        if (power_reject(recog)) {
            result_error(recog, J_RESULT_STATUS_REJECT_POWER);
            goto end_recog;
        }
#endif

        /* if terminate signal has been received, cancel this input */
        if (recog->process_want_terminate) {
            result_error(recog, J_RESULT_STATUS_TERMINATE);
            goto end_recog;
        }

        /* if GMM is specified and result are to be rejected, terminate search here */
        if (jconf->reject.gmm_reject_cmn_string != NULL) {
            if (!gmm_valid_input(recog)) {
                result_error(recog, J_RESULT_STATUS_REJECT_GMM);
                goto end_recog;
            }
        }

        /* for instances with "-1pass", copy 1st pass result as final */
        /* execute stack-decoding search */
        /* they will be skipepd in the next pass */
        for (r = recog->process_list; r; r = r->next) {
            if (!r->live) continue;
            /* skip if 1st pass was failed */
            if (r->result.status < 0) continue;
            /* already stored on word recognition, so skip this */
            if (r->lmvar == LM_DFA_WORD) continue;
            if (r->config->compute_only_1pass) {
                if (verbose_flag) {
                    jlog("%02d %s: \"-1pass\" specified, output 1st pass result as a final result\n", r->config->id, r->config->name);
                }
                /* prepare result storage */
                result_sentence_malloc(r, 1);
                /* finalize result when no hypothesis was obtained */
                pass2_finalize_on_no_result(r, TRUE);
            }
        }

        /***********************************************/
        /* 2nd-pass --- forward search with heuristics */
        /***********************************************/
        pass2_p = FALSE;
        for (r = recog->process_list; r; r = r->next) {
            if (!r->live) continue;
            /* if [-1pass] is specified, skip 2nd pass */
            if (r->config->compute_only_1pass) continue;
            /* if search already failed on 1st pass, skip 2nd pass */
            if (r->result.status < 0) continue;
            pass2_p = TRUE;
        }
        if (pass2_p) callback_exec(CALLBACK_EVENT_PASS2_BEGIN, recog);

#if !defined(PASS2_STRICT_IWCD) || defined(FIX_35_PASS2_STRICT_SCORE)    
        /* adjust trellis score not to contain outprob of the last frames */
        for (r = recog->process_list; r; r = r->next) {
            if (!r->live) continue;
            /* if [-1pass] is specified, skip 2nd pass */
            if (r->config->compute_only_1pass) continue;
            /* if search already failed on 1st pass, skip 2nd pass */
            if (r->result.status < 0) continue;
            if (!r->am->hmminfo->multipath) {
                bt_discount_pescore(r->wchmm, r->backtrellis, r->am->mfcc->param);
            }
#ifdef LM_FIX_DOUBLE_SCORING
            if (r->lmtype == LM_PROB) {
                bt_discount_lm(r->backtrellis);
            }
#endif
        }
#endif

        /* execute stack-decoding search */
        for (r = recog->process_list; r; r = r->next) {
            if (!r->live) continue;
            /* if [-1pass] is specified, just copy from 1st pass result */
            if (r->config->compute_only_1pass) continue;
            /* if search already failed on 1st pass, skip 2nd pass */
            if (r->result.status < 0) continue;
            /* prepare result storage */
            if (r->lmtype == LM_DFA && r->config->output.multigramout_flag) {
                result_sentence_malloc(r, r->config->output.output_hypo_maxnum * multigram_get_all_num(r->lm));
            } else {
                result_sentence_malloc(r, r->config->output.output_hypo_maxnum);
            }
            /* do 2nd pass */
            if (r->lmtype == LM_PROB) {
                wchmm_fbs(r->am->mfcc->param, r, 0, 0);
            } else if (r->lmtype == LM_DFA) {
                if (r->config->output.multigramout_flag) {
                    /* execute 2nd pass multiple times for each grammar sequencially */
                    /* to output result for each grammar */
                    MULTIGRAM *m;
                    boolean has_success = FALSE;
                    for (m = r->lm->grammars; m; m = m->next) {
                        if (m->active) {
                            jlog("STAT: execute 2nd pass limiting words for gram #%d\n", m->id);
                            wchmm_fbs(r->am->mfcc->param, r, m->cate_begin, m->dfa->term_num);
                            if (r->result.status == J_RESULT_STATUS_SUCCESS) {
                                has_success = TRUE;
                            }
                        }
                    }
                    r->result.status = (has_success == TRUE) ? J_RESULT_STATUS_SUCCESS : J_RESULT_STATUS_FAIL;
                } else {
                    /* only the best among all grammar will be output */
                    wchmm_fbs(r->am->mfcc->param, r, 0, r->lm->dfa->term_num);
                }
            }
        }

        /* do forced alignment if needed */
        for (r = recog->process_list; r; r = r->next) {
            if (!r->live) continue;
            /* if search failed on 2nd pass, skip this */
            if (r->result.status < 0) continue;
            /* do needed alignment */
            do_alignment_all(r, r->am->mfcc->param);
        }

        /* output result */
        callback_exec(CALLBACK_RESULT, recog);
#ifdef ENABLE_PLUGIN
        plugin_exec_process_result(recog);
#endif
        /* output graph */
        /* r->result.wg == NULL should be skipped inside the callback */
        ok_p = FALSE;
        for (r = recog->process_list; r; r = r->next) {
            if (!r->live) continue;
            if (r->config->compute_only_1pass) continue;
            if (r->result.status < 0) continue;
            if (r->config->graph.lattice) ok_p = TRUE;
        }
        if (ok_p) callback_exec(CALLBACK_RESULT_GRAPH, recog);
        /* output confnet */
        /* r->result.confnet == NULL should be skipped inside the callback */
        ok_p = FALSE;
        for (r = recog->process_list; r; r = r->next) {
            if (!r->live) continue;
            if (r->config->compute_only_1pass) continue;
            if (r->result.status < 0) continue;
            if (r->config->graph.confnet) ok_p = TRUE;
        }
        if (ok_p) callback_exec(CALLBACK_RESULT_CONFNET, recog);

        /* clear work area for output */
        for (r = recog->process_list; r; r = r->next) {
            if (!r->live) continue;
            clear_result(r);
        }

        /* output end of 2nd pass */
        if (pass2_p) callback_exec(CALLBACK_EVENT_PASS2_END, recog);

#ifdef DEBUG_VTLN_ALPHA_TEST
        if (r->am->mfcc->para->vtln_alpha == 1.0) {
            /* if vtln parameter remains default, search for VTLN parameter */
            vtln_alpha(recog, r);
        }
#endif

end_recog:
        /**********************/
        /* end of recognition */
        /**********************/

        /* update CMN info for next input (in case of realtime wave input) */
        if (jconf->input.type == INPUT_WAVEFORM && jconf->decodeopt.realtime_flag) {
            for (mfcc = recog->mfcclist; mfcc; mfcc = mfcc->next) {
                if (mfcc->param->samplenum > 0) {
                    RealTimeCMNUpdate(mfcc, recog);
                }
            }
        }

        process_segment_last = recog->process_segment;
        if (jconf->decodeopt.segment) { /* sp-segment mode */
            /* param is now shrinked to hold only the processed input, and */
            /* the rests are holded in (newly allocated) "rest_param" */
            /* if this is the last segment, rest_param is NULL */
            /* assume all segmentation are synchronized */
            recog->process_segment = FALSE;
            for (mfcc = recog->mfcclist; mfcc; mfcc = mfcc->next) {
                if (mfcc->rest_param != NULL) {
                    /* process the rest parameters in the next loop */
                    recog->process_segment = TRUE;
                    free_param(mfcc->param);
                    mfcc->param = mfcc->rest_param;
                    mfcc->rest_param = NULL;
                }
            }
        }

        /* callback of recognition end */
        if (jconf->decodeopt.segment) {
#ifdef BACKEND_VAD
            if (recog->triggered) callback_exec(CALLBACK_EVENT_SEGMENT_END, recog);
            if (process_segment_last && !recog->process_segment) callback_exec(CALLBACK_EVENT_RECOGNITION_END, recog);
#else
            callback_exec(CALLBACK_EVENT_SEGMENT_END, recog);
            if (!recog->process_segment) callback_exec(CALLBACK_EVENT_RECOGNITION_END, recog);
#endif
        } else {
            callback_exec(CALLBACK_EVENT_RECOGNITION_END, recog);
        }


        if (verbose_flag) jlog("\n");
        jlog_flush();

        if (jconf->decodeopt.segment) { /* sp-segment mode */
            if (recog->process_segment == TRUE) {
                if (verbose_flag) jlog("STAT: <<<restart the rest>>>\n\n");
            } else {
                /* input has reached end of stream, terminate program */
                if (ret <= 0 && ret != -2) break;
            }
        } else { /* not sp-segment mode */
            /* input has reached end of stream, terminate program */
            if (ret <= 0 && ret != -2) break;
        }

        /* recognition continues for next (silence-aparted) segment */

    } /* END OF STREAM LOOP */

    /* close the stream */
    if (jconf->input.type == INPUT_WAVEFORM) {
        if (adin_end(recog->adin) == FALSE) return -1;
    }
    if (jconf->input.speech_input == SP_MFCMODULE) {
        if (mfc_module_end(recog->mfcclist) == FALSE) return -1;
    }

    /* return to the opening of input stream */

    return (0);

}


/**
 * Main function
 * 
 */
int
main(int argc, char *argv[]) {
    Jconf *jconf;
    Recog *recog;

    int ret;

    //j_enable_debug_message();
    j_enable_verbose_message();

    /* if no argument, output usage and exit */
    if (argc == 1) {
        fprintf(stderr, "Julius rev.%s - based on ", JULIUS_VERSION);
        j_put_version(stderr);
        fprintf(stderr, "Try '-setting' for built-in engine configuration.\n");
        fprintf(stderr, "Try '-help' for run time options.\n");
        return -1;
    }

    /* 1. load configurations from command arguments */
    jconf = j_config_load_args_new(argc, argv);
    if (jconf == NULL) { /* error */
        fprintf(stderr, "Try `-help' for more information.\n");
        return -1;
    }

    /* 2. create recognition instance according to the jconf */
    recog = j_create_instance_from_jconf(jconf);
    if (recog == NULL) {
        fprintf(stderr, "Error in startup\n");
        return -1;
    }

    /* register result callback functions */
    callback_add(recog, CALLBACK_EVENT_SPEECH_READY, status_recready, NULL);
    callback_add(recog, CALLBACK_EVENT_SPEECH_START, status_recstart, NULL);
    callback_add(recog, CALLBACK_RESULT, output_result, NULL);

    /**************************/
    /* Initialize audio input */
    /**************************/
    /* initialize audio input device */
    /* ad-in thread starts at this time for microphone */
    if (j_adin_init(recog) == FALSE) { /* error */
        return -1;
    }
    // force null input
    recog->adin->ad_standby = NULL;
    recog->adin->ad_begin = NULL;
    recog->adin->ad_end   = NULL;
    recog->adin->ad_read  = NULL;
    recog->adin->ad_input_name = "Push function";
/*
    // raw speech input (microphone etc.)
    switch (j_open_stream(recog, NULL)) {
        case 0: // succeeded
            break;
        case -1: // error
            fprintf(stderr, "error in input stream\n");
            return;
        case -2: // end of recognition process
            fprintf(stderr, "failed to begin input stream\n");
            return;
    }
*/
    /**********************/
    /* Recognization Loop */
    /**********************/
    /* enter main loop to recognize the input stream */
    /* finish after whole input has been processed and input reaches end */
    //    ret = j_recognize_stream(recog);
    //    ret = j_recognize_stream_core(recog);
    RealTimeInit(recog);
    ret = _test_recognize_stream(recog);


    if (ret == -1) return -1; /* error */


    /* calling j_close_stream(recog) at any time will terminate
       recognition and exit j_recognize_stream() */
    j_close_stream(recog);

    j_recog_free(recog);

    /* exit program */
    return (0);
}
