ps_nbest_t * ps_nbest(ps_decoder_t *ps, int sf, int ef, char const *ctx1, char const *ctx2) { ps_lattice_t *dag; ngram_model_t *lmset; ps_astar_t *nbest; float32 lwf; int32 w1, w2; if (ps->search == NULL) return NULL; if ((dag = ps_get_lattice(ps)) == NULL) return NULL; /* FIXME: This is all quite specific to N-Gram search. Either we * should make N-best a method for each search module or it needs * to be abstracted to work for N-Gram and FSG. */ if (0 != strcmp(ps_search_name(ps->search), PS_SEARCH_NGRAM)) { lmset = NULL; lwf = 1.0f; } else { lmset = ((ngram_search_t *)ps->search)->lmset; lwf = ((ngram_search_t *)ps->search)->bestpath_fwdtree_lw_ratio; } w1 = ctx1 ? dict_wordid(ps_search_dict(ps->search), ctx1) : -1; w2 = ctx2 ? dict_wordid(ps_search_dict(ps->search), ctx2) : -1; nbest = ps_astar_start(dag, lmset, lwf, sf, ef, w1, w2); return (ps_nbest_t *)nbest; }
static void ngram_fwdflat_expand_all(ngram_search_t *ngs) { int n_words, i; /* For all "real words" (not fillers or <s>/</s>) in the dictionary, * * 1) Add the ones which are in the LM to the fwdflat wordlist * 2) And to the expansion list (since we are expanding all) */ ngs->n_expand_words = 0; n_words = ps_search_n_words(ngs); bitvec_clear_all(ngs->expand_word_flag, ps_search_n_words(ngs)); for (i = 0; i < n_words; ++i) { if (!ngram_model_set_known_wid(ngs->lmset, dict_basewid(ps_search_dict(ngs),i))) continue; ngs->fwdflat_wordlist[ngs->n_expand_words] = i; ngs->expand_word_list[ngs->n_expand_words] = i; bitvec_set(ngs->expand_word_flag, i); ngs->n_expand_words++; } E_INFO("Utterance vocabulary contains %d words\n", ngs->n_expand_words); ngs->expand_word_list[ngs->n_expand_words] = -1; ngs->fwdflat_wordlist[ngs->n_expand_words] = -1; }
/** * Free HMM network for one utterance of fwdflat search. */ static void destroy_fwdflat_chan(ngram_search_t *ngs) { int32 i, wid; for (i = 0; ngs->fwdflat_wordlist[i] >= 0; i++) { root_chan_t *rhmm; chan_t *thmm; wid = ngs->fwdflat_wordlist[i]; if (dict_is_single_phone(ps_search_dict(ngs),wid)) continue; assert(ngs->word_chan[wid] != NULL); /* The first HMM in ngs->word_chan[wid] was allocated with * ngs->root_chan_alloc, but this will attempt to free it * using ngs->chan_alloc, which will not work. Therefore we * free it manually and move the list forward before handing * it off. */ rhmm = (root_chan_t *)ngs->word_chan[wid]; thmm = rhmm->next; listelem_free(ngs->root_chan_alloc, rhmm); ngs->word_chan[wid] = thmm; ngram_search_free_all_rc(ngs, wid); } }
int ngram_fwdflat_reinit(ngram_search_t *ngs) { /* Reallocate things that depend on the number of words. */ int n_words; ckd_free(ngs->fwdflat_wordlist); ckd_free(ngs->expand_word_list); bitvec_free(ngs->expand_word_flag); n_words = ps_search_n_words(ngs); ngs->fwdflat_wordlist = ckd_calloc(n_words + 1, sizeof(*ngs->fwdflat_wordlist)); ngs->expand_word_flag = bitvec_alloc(n_words); ngs->expand_word_list = ckd_calloc(n_words + 1, sizeof(*ngs->expand_word_list)); /* No tree-search; take care of the expansion list and single phone words. */ if (!ngs->fwdtree) { /* Free single-phone words. */ ngram_fwdflat_free_1ph(ngs); /* Reallocate word_chan. */ ckd_free(ngs->word_chan); ngs->word_chan = ckd_calloc(dict_size(ps_search_dict(ngs)), sizeof(*ngs->word_chan)); /* Rebuild full expansion list from LM words. */ ngram_fwdflat_expand_all(ngs); /* Allocate single phone words. */ ngram_fwdflat_allocate_1ph(ngs); } /* Otherwise there is nothing to do since the wordlist is * generated anew every utterance. */ return 0; }
ps_search_t * phone_loop_search_init(cmd_ln_t *config, acmod_t *acmod, dict_t *dict) { phone_loop_search_t *pls; /* Allocate and initialize. */ pls = ckd_calloc(1, sizeof(*pls)); ps_search_init(ps_search_base(pls), &phone_loop_search_funcs, config, acmod, dict, NULL); phone_loop_search_reinit(ps_search_base(pls), ps_search_dict(pls), ps_search_dict2pid(pls)); return ps_search_base(pls); }
static void ngram_fwdflat_free_1ph(ngram_search_t *ngs) { int i, w; int n_words = ps_search_n_words(ngs); for (i = w = 0; w < n_words; ++w) { if (!dict_is_single_phone(ps_search_dict(ngs), w)) continue; hmm_deinit(&ngs->rhmm_1ph[i].hmm); ++i; } ckd_free(ngs->rhmm_1ph); ngs->rhmm_1ph = NULL; ckd_free(ngs->single_phone_wid); }
ps_search_t * phone_loop_search_init(cmd_ln_t *config, acmod_t *acmod, dict_t *dict) { phone_loop_search_t *pls; /* Allocate and initialize. */ pls = (phone_loop_search_t *)ckd_calloc(1, sizeof(*pls)); ps_search_init(ps_search_base(pls), &phone_loop_search_funcs, PS_SEARCH_TYPE_PHONE_LOOP, PS_DEFAULT_PL_SEARCH, config, acmod, dict, NULL); phone_loop_search_reinit(ps_search_base(pls), ps_search_dict(pls), ps_search_dict2pid(pls)); return ps_search_base(pls); }
static void ngram_fwdflat_allocate_1ph(ngram_search_t *ngs) { dict_t *dict = ps_search_dict(ngs); int n_words = ps_search_n_words(ngs); int i, w; /* Allocate single-phone words, since they won't have * been allocated for us by fwdtree initialization. */ ngs->n_1ph_words = 0; for (w = 0; w < n_words; w++) { if (dict_is_single_phone(dict, w)) ++ngs->n_1ph_words; } ngs->single_phone_wid = ckd_calloc(ngs->n_1ph_words, sizeof(*ngs->single_phone_wid)); ngs->rhmm_1ph = ckd_calloc(ngs->n_1ph_words, sizeof(*ngs->rhmm_1ph)); i = 0; for (w = 0; w < n_words; w++) { if (!dict_is_single_phone(dict, w)) continue; /* DICT2PID location */ ngs->rhmm_1ph[i].ciphone = dict_first_phone(dict, w); ngs->rhmm_1ph[i].ci2phone = bin_mdef_silphone(ps_search_acmod(ngs)->mdef); hmm_init(ngs->hmmctx, &ngs->rhmm_1ph[i].hmm, TRUE, /* ssid */ bin_mdef_pid2ssid(ps_search_acmod(ngs)->mdef, ngs->rhmm_1ph[i].ciphone), /* tmatid */ bin_mdef_pid2tmatid(ps_search_acmod(ngs)->mdef, ngs->rhmm_1ph[i].ciphone)); ngs->rhmm_1ph[i].next = NULL; ngs->word_chan[w] = (chan_t *) &(ngs->rhmm_1ph[i]); ngs->single_phone_wid[i] = w; i++; } }
static void fwdflat_word_transition(ngram_search_t *ngs, int frame_idx) { int32 cf, nf, b, thresh, pip, i, w, newscore; int32 best_silrc_score = 0, best_silrc_bp = 0; /* FIXME: good defaults? */ bptbl_t *bp; int32 *rcss; root_chan_t *rhmm; int32 *awl; float32 lwf; dict_t *dict = ps_search_dict(ngs); dict2pid_t *d2p = ps_search_dict2pid(ngs); cf = frame_idx; nf = cf + 1; thresh = ngs->best_score + ngs->fwdflatbeam; pip = ngs->pip; best_silrc_score = WORST_SCORE; lwf = ngs->fwdflat_fwdtree_lw_ratio; /* Search for all words starting within a window of this frame. * These are the successors for words exiting now. */ get_expand_wordlist(ngs, cf, ngs->max_sf_win); /* Scan words exited in current frame */ for (b = ngs->bp_table_idx[cf]; b < ngs->bpidx; b++) { xwdssid_t *rssid; int32 silscore; bp = ngs->bp_table + b; ngs->word_lat_idx[bp->wid] = NO_BP; if (bp->wid == ps_search_finish_wid(ngs)) continue; /* DICT2PID location */ /* Get the mapping from right context phone ID to index in the * right context table and the bscore_stack. */ rcss = ngs->bscore_stack + bp->s_idx; if (bp->last2_phone == -1) rssid = NULL; else rssid = dict2pid_rssid(d2p, bp->last_phone, bp->last2_phone); /* Transition to all successor words. */ for (i = 0; ngs->expand_word_list[i] >= 0; i++) { int32 n_used; w = ngs->expand_word_list[i]; /* Get the exit score we recorded in save_bwd_ptr(), or * something approximating it. */ if (rssid) newscore = rcss[rssid->cimap[dict_first_phone(dict, w)]]; else newscore = bp->score; if (newscore == WORST_SCORE) continue; /* FIXME: Floating point... */ newscore += lwf * (ngram_tg_score(ngs->lmset, dict_basewid(dict, w), bp->real_wid, bp->prev_real_wid, &n_used) >> SENSCR_SHIFT); newscore += pip; /* Enter the next word */ if (newscore BETTER_THAN thresh) { rhmm = (root_chan_t *) ngs->word_chan[w]; if ((hmm_frame(&rhmm->hmm) < cf) || (newscore BETTER_THAN hmm_in_score(&rhmm->hmm))) { hmm_enter(&rhmm->hmm, newscore, b, nf); /* DICT2PID: This is where mpx ssids get introduced. */ /* Look up the ssid to use when entering this mpx triphone. */ hmm_mpx_ssid(&rhmm->hmm, 0) = dict2pid_ldiph_lc(d2p, rhmm->ciphone, rhmm->ci2phone, dict_last_phone(dict, bp->wid)); assert(IS_S3SSID(hmm_mpx_ssid(&rhmm->hmm, 0))); E_DEBUG(6,("ssid %d(%d,%d) = %d\n", rhmm->ciphone, dict_last_phone(dict, bp->wid), rhmm->ci2phone, hmm_mpx_ssid(&rhmm->hmm, 0))); bitvec_set(ngs->word_active, w); } } } /* Get the best exit into silence. */ if (rssid) silscore = rcss[rssid->cimap[ps_search_acmod(ngs)->mdef->sil]]; else silscore = bp->score; if (silscore BETTER_THAN best_silrc_score) { best_silrc_score = silscore; best_silrc_bp = b; } } /* Transition to <sil> */ newscore = best_silrc_score + ngs->silpen + pip; if ((newscore BETTER_THAN thresh) && (newscore BETTER_THAN WORST_SCORE)) { w = ps_search_silence_wid(ngs); rhmm = (root_chan_t *) ngs->word_chan[w]; if ((hmm_frame(&rhmm->hmm) < cf) || (newscore BETTER_THAN hmm_in_score(&rhmm->hmm))) { hmm_enter(&rhmm->hmm, newscore, best_silrc_bp, nf); bitvec_set(ngs->word_active, w); } } /* Transition to noise words */ newscore = best_silrc_score + ngs->fillpen + pip; if ((newscore BETTER_THAN thresh) && (newscore BETTER_THAN WORST_SCORE)) { for (w = ps_search_silence_wid(ngs) + 1; w < ps_search_n_words(ngs); w++) { rhmm = (root_chan_t *) ngs->word_chan[w]; /* Noise words that aren't a single phone will have NULL here. */ if (rhmm == NULL) continue; if ((hmm_frame(&rhmm->hmm) < cf) || (newscore BETTER_THAN hmm_in_score(&rhmm->hmm))) { hmm_enter(&rhmm->hmm, newscore, best_silrc_bp, nf); bitvec_set(ngs->word_active, w); } } } /* Reset initial channels of words that have become inactive even after word trans. */ i = ngs->n_active_word[cf & 0x1]; awl = ngs->active_word_list[cf & 0x1]; for (w = *(awl++); i > 0; --i, w = *(awl++)) { rhmm = (root_chan_t *) ngs->word_chan[w]; if (hmm_frame(&rhmm->hmm) == cf) { hmm_clear_scores(&rhmm->hmm); } } }
static void fwdflat_prune_chan(ngram_search_t *ngs, int frame_idx) { int32 i, cf, nf, w, pip, newscore, thresh, wordthresh; int32 *awl; root_chan_t *rhmm; chan_t *hmm, *nexthmm; cf = frame_idx; nf = cf + 1; i = ngs->n_active_word[cf & 0x1]; awl = ngs->active_word_list[cf & 0x1]; bitvec_clear_all(ngs->word_active, ps_search_n_words(ngs)); thresh = ngs->best_score + ngs->fwdflatbeam; wordthresh = ngs->best_score + ngs->fwdflatwbeam; pip = ngs->pip; E_DEBUG(3,("frame %d thresh %d wordthresh %d\n", frame_idx, thresh, wordthresh)); /* Scan all active words. */ for (w = *(awl++); i > 0; --i, w = *(awl++)) { rhmm = (root_chan_t *) ngs->word_chan[w]; /* Propagate active root channels */ if (hmm_frame(&rhmm->hmm) == cf && hmm_bestscore(&rhmm->hmm) BETTER_THAN thresh) { hmm_frame(&rhmm->hmm) = nf; bitvec_set(ngs->word_active, w); /* Transitions out of root channel */ newscore = hmm_out_score(&rhmm->hmm); if (rhmm->next) { assert(!dict_is_single_phone(ps_search_dict(ngs), w)); newscore += pip; if (newscore BETTER_THAN thresh) { hmm = rhmm->next; /* Enter all right context phones */ if (hmm->info.rc_id >= 0) { for (; hmm; hmm = hmm->next) { if ((hmm_frame(&hmm->hmm) < cf) || (newscore BETTER_THAN hmm_in_score(&hmm->hmm))) { hmm_enter(&hmm->hmm, newscore, hmm_out_history(&rhmm->hmm), nf); } } } /* Just a normal word internal phone */ else { if ((hmm_frame(&hmm->hmm) < cf) || (newscore BETTER_THAN hmm_in_score(&hmm->hmm))) { hmm_enter(&hmm->hmm, newscore, hmm_out_history(&rhmm->hmm), nf); } } } } else { assert(dict_is_single_phone(ps_search_dict(ngs), w)); /* Word exit for single-phone words (where did their * whmms come from?) (either from * ngram_search_fwdtree, or from * ngram_fwdflat_allocate_1ph(), that's where) */ if (newscore BETTER_THAN wordthresh) { ngram_search_save_bp(ngs, cf, w, newscore, hmm_out_history(&rhmm->hmm), 0); } } } /* Transitions out of non-root channels. */ for (hmm = rhmm->next; hmm; hmm = hmm->next) { if (hmm_frame(&hmm->hmm) >= cf) { /* Propagate forward HMMs inside the beam. */ if (hmm_bestscore(&hmm->hmm) BETTER_THAN thresh) { hmm_frame(&hmm->hmm) = nf; bitvec_set(ngs->word_active, w); newscore = hmm_out_score(&hmm->hmm); /* Word-internal phones */ if (hmm->info.rc_id < 0) { newscore += pip; if (newscore BETTER_THAN thresh) { nexthmm = hmm->next; /* Enter all right-context phones. */ if (nexthmm->info.rc_id >= 0) { for (; nexthmm; nexthmm = nexthmm->next) { if ((hmm_frame(&nexthmm->hmm) < cf) || (newscore BETTER_THAN hmm_in_score(&nexthmm->hmm))) { hmm_enter(&nexthmm->hmm, newscore, hmm_out_history(&hmm->hmm), nf); } } } /* Enter single word-internal phone. */ else { if ((hmm_frame(&nexthmm->hmm) < cf) || (newscore BETTER_THAN hmm_in_score(&nexthmm->hmm))) { hmm_enter(&nexthmm->hmm, newscore, hmm_out_history(&hmm->hmm), nf); } } } } /* Right-context phones - apply word beam and exit. */ else { if (newscore BETTER_THAN wordthresh) { ngram_search_save_bp(ngs, cf, w, newscore, hmm_out_history(&hmm->hmm), hmm->info.rc_id); } } } /* Zero out inactive HMMs. */ else if (hmm_frame(&hmm->hmm) != nf) { hmm_clear_scores(&hmm->hmm); } } } } }
/** * Build HMM network for one utterance of fwdflat search. */ static void build_fwdflat_chan(ngram_search_t *ngs) { int32 i, wid, p; root_chan_t *rhmm; chan_t *hmm, *prevhmm; dict_t *dict; dict2pid_t *d2p; dict = ps_search_dict(ngs); d2p = ps_search_dict2pid(ngs); /* Build word HMMs for each word in the lattice. */ for (i = 0; ngs->fwdflat_wordlist[i] >= 0; i++) { wid = ngs->fwdflat_wordlist[i]; /* Single-phone words are permanently allocated */ if (dict_is_single_phone(dict, wid)) continue; assert(ngs->word_chan[wid] == NULL); /* Multiplex root HMM for first phone (one root per word, flat * lexicon). diphone is irrelevant here, for the time being, * at least. */ rhmm = listelem_malloc(ngs->root_chan_alloc); rhmm->ci2phone = dict_second_phone(dict, wid); rhmm->ciphone = dict_first_phone(dict, wid); rhmm->next = NULL; hmm_init(ngs->hmmctx, &rhmm->hmm, TRUE, bin_mdef_pid2ssid(ps_search_acmod(ngs)->mdef, rhmm->ciphone), bin_mdef_pid2tmatid(ps_search_acmod(ngs)->mdef, rhmm->ciphone)); /* HMMs for word-internal phones */ prevhmm = NULL; for (p = 1; p < dict_pronlen(dict, wid) - 1; p++) { hmm = listelem_malloc(ngs->chan_alloc); hmm->ciphone = dict_pron(dict, wid, p); hmm->info.rc_id = (p == dict_pronlen(dict, wid) - 1) ? 0 : -1; hmm->next = NULL; hmm_init(ngs->hmmctx, &hmm->hmm, FALSE, dict2pid_internal(d2p,wid,p), bin_mdef_pid2tmatid(ps_search_acmod(ngs)->mdef, hmm->ciphone)); if (prevhmm) prevhmm->next = hmm; else rhmm->next = hmm; prevhmm = hmm; } /* Right-context phones */ ngram_search_alloc_all_rc(ngs, wid); /* Link in just allocated right-context phones */ if (prevhmm) prevhmm->next = ngs->word_chan[wid]; else rhmm->next = ngs->word_chan[wid]; ngs->word_chan[wid] = (chan_t *) rhmm; } }
/** * Find all active words in backpointer table and sort by frame. */ static void build_fwdflat_wordlist(ngram_search_t *ngs) { int32 i, f, sf, ef, wid, nwd; bptbl_t *bp; ps_latnode_t *node, *prevnode, *nextnode; /* No tree-search, use statically allocated wordlist. */ if (!ngs->fwdtree) return; memset(ngs->frm_wordlist, 0, ngs->n_frame_alloc * sizeof(*ngs->frm_wordlist)); /* Scan the backpointer table for all active words and record * their exit frames. */ for (i = 0, bp = ngs->bp_table; i < ngs->bpidx; i++, bp++) { sf = (bp->bp < 0) ? 0 : ngs->bp_table[bp->bp].frame + 1; ef = bp->frame; wid = bp->wid; /* Anything that can be transitioned to in the LM can go in * the word list. */ if (!ngram_model_set_known_wid(ngs->lmset, dict_basewid(ps_search_dict(ngs), wid))) continue; /* Look for it in the wordlist. */ for (node = ngs->frm_wordlist[sf]; node && (node->wid != wid); node = node->next); /* Update last end frame. */ if (node) node->lef = ef; else { /* New node; link to head of list */ node = listelem_malloc(ngs->latnode_alloc); node->wid = wid; node->fef = node->lef = ef; node->next = ngs->frm_wordlist[sf]; ngs->frm_wordlist[sf] = node; } } /* Eliminate "unlikely" words, for which there are too few end points */ for (f = 0; f < ngs->n_frame; f++) { prevnode = NULL; for (node = ngs->frm_wordlist[f]; node; node = nextnode) { nextnode = node->next; /* Word has too few endpoints */ if ((node->lef - node->fef < ngs->min_ef_width) || /* Word is </s> and doesn't actually end in last frame */ ((node->wid == ps_search_finish_wid(ngs)) && (node->lef < ngs->n_frame - 1))) { if (!prevnode) ngs->frm_wordlist[f] = nextnode; else prevnode->next = nextnode; listelem_free(ngs->latnode_alloc, node); } else prevnode = node; } } /* Form overall wordlist for 2nd pass */ nwd = 0; bitvec_clear_all(ngs->word_active, ps_search_n_words(ngs)); for (f = 0; f < ngs->n_frame; f++) { for (node = ngs->frm_wordlist[f]; node; node = node->next) { if (!bitvec_is_set(ngs->word_active, node->wid)) { bitvec_set(ngs->word_active, node->wid); ngs->fwdflat_wordlist[nwd++] = node->wid; } } } ngs->fwdflat_wordlist[nwd] = -1; E_INFO("Utterance vocabulary contains %d words\n", nwd); }