/* -*- c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* ==================================================================== * Copyright (c) 1999-2010 Carnegie Mellon University. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY * NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * ==================================================================== * */ /** * @file fsg_lextree.c * @brief The collection of all the lextrees for the entire FSM. * @author M K Ravishankar * @author Bhiksha Raj */ /* System headers. */ #include #include #include /* SphinxBase headers. */ #include #include /* Local headers. */ #include "fsg_lextree.h" #define __FSG_DBG__ 0 /* A linklist structure that is actually used to build local lextrees at grammar nodes */ typedef struct fsg_glist_linklist_t { int32 ci, rc; glist_t glist; struct fsg_glist_linklist_t *next; } fsg_glist_linklist_t; /** * Build the phone lextree for all transitions out of state from_state. * Return the root node of this tree. * Also, return a linear linked list of all allocated fsg_pnode_t nodes in * *alloc_head (for memory management purposes). */ static fsg_pnode_t *fsg_psubtree_init(fsg_lextree_t *tree, fsg_model_t *fsg, int32 from_state, fsg_pnode_t **alloc_head); /** * Free the given lextree. alloc_head: head of linear list of allocated * nodes updated by fsg_psubtree_init(). */ static void fsg_psubtree_free(fsg_pnode_t *alloc_head); /** * Dump the list of nodes in the given lextree to the given file. alloc_head: * head of linear list of allocated nodes updated by fsg_psubtree_init(). */ static void fsg_psubtree_dump(fsg_lextree_t *tree, fsg_pnode_t *root, FILE *fp); /** * Compute the left and right context CIphone sets for each state. */ static void fsg_lextree_lc_rc(fsg_lextree_t *lextree) { int32 s, i, j; int32 n_ci; fsg_model_t *fsg; int32 silcipid; int32 len; silcipid = bin_mdef_silphone(lextree->mdef); assert(silcipid >= 0); n_ci = bin_mdef_n_ciphone(lextree->mdef); fsg = lextree->fsg; /* * lextree->lc[s] = set of left context CIphones for state s. Similarly, rc[s] * for right context CIphones. */ lextree->lc = ckd_calloc_2d(fsg->n_state, n_ci + 1, sizeof(**lextree->lc)); lextree->rc = ckd_calloc_2d(fsg->n_state, n_ci + 1, sizeof(**lextree->rc)); E_INFO("Allocated %d bytes (%d KiB) for left and right context phones\n", fsg->n_state * (n_ci + 1) * 2, fsg->n_state * (n_ci + 1) * 2 / 1024); for (s = 0; s < fsg->n_state; s++) { fsg_arciter_t *itor; for (itor = fsg_model_arcs(fsg, s); itor; itor = fsg_arciter_next(itor)) { fsg_link_t *l = fsg_arciter_get(itor); int32 dictwid; /**< Dictionary (not FSG) word ID!! */ if (fsg_link_wid(l) >= 0) { dictwid = dict_wordid(lextree->dict, fsg_model_word_str(lextree->fsg, l->wid)); /* * Add the first CIphone of l->wid to the rclist of state s, and * the last CIphone to lclist of state d. * (Filler phones are a pain to deal with. There is no direct * marking of a filler phone; but only filler words are supposed to * use such phones, so we use that fact. HACK!! FRAGILE!!) * * UPD: tests carsh here if .fsg model used with wrong hmm and * dictionary */ if (fsg_model_is_filler(fsg, fsg_link_wid(l))) { /* Filler phone; use silence phone as context */ lextree->rc[fsg_link_from_state(l)][silcipid] = 1; lextree->lc[fsg_link_to_state(l)][silcipid] = 1; } else { len = dict_pronlen(lextree->dict, dictwid); lextree->rc[fsg_link_from_state(l)][dict_pron(lextree->dict, dictwid, 0)] = 1; lextree->lc[fsg_link_to_state(l)][dict_pron(lextree->dict, dictwid, len - 1)] = 1; } } } } for (s = 0; s < fsg->n_state; s++) { /* * Add SIL phone to the lclist and rclist of each state. Strictly * speaking, only needed at start and final states, respectively, but * all states considered since the user may change the start and final * states. In any case, most applications would have a silence self * loop at each state, hence these would be needed anyway. */ lextree->lc[s][silcipid] = 1; lextree->rc[s][silcipid] = 1; } /* * Propagate lc and rc lists past null transitions. (Since FSG contains * null transitions closure, no need to worry about a chain of successive * null transitions. Right??) * * This can't be joined with the previous loop because we first calculate * contexts and only then we can propagate them. */ for (s = 0; s < fsg->n_state; s++) { fsg_arciter_t *itor; for (itor = fsg_model_arcs(fsg, s); itor; itor = fsg_arciter_next(itor)) { fsg_link_t *l = fsg_arciter_get(itor); if (fsg_link_wid(l) < 0) { /* * lclist(d) |= lclist(s), because all the words ending up at s, can * now also end at d, becoming the left context for words leaving d. */ for (i = 0; i < n_ci; i++) lextree->lc[fsg_link_to_state(l)][i] |= lextree->lc[fsg_link_from_state(l)][i]; /* * Similarly, rclist(s) |= rclist(d), because all the words leaving d * can equivalently leave s, becoming the right context for words * ending up at s. */ for (i = 0; i < n_ci; i++) lextree->rc[fsg_link_from_state(l)][i] |= lextree->rc[fsg_link_to_state(l)][i]; } } } /* Convert the bit-vector representation into a list */ for (s = 0; s < fsg->n_state; s++) { j = 0; for (i = 0; i < n_ci; i++) { if (lextree->lc[s][i]) { lextree->lc[s][j] = i; j++; } } lextree->lc[s][j] = -1; /* Terminate the list */ j = 0; for (i = 0; i < n_ci; i++) { if (lextree->rc[s][i]) { lextree->rc[s][j] = i; j++; } } lextree->rc[s][j] = -1; /* Terminate the list */ } } /* * For now, allocate the entire lextree statically. */ fsg_lextree_t * fsg_lextree_init(fsg_model_t * fsg, dict_t *dict, dict2pid_t *d2p, bin_mdef_t *mdef, hmm_context_t *ctx, int32 wip, int32 pip) { int32 s, n_leaves; fsg_lextree_t *lextree; fsg_pnode_t *pn; lextree = ckd_calloc(1, sizeof(fsg_lextree_t)); lextree->fsg = fsg; lextree->root = ckd_calloc(fsg_model_n_state(fsg), sizeof(fsg_pnode_t *)); lextree->alloc_head = ckd_calloc(fsg_model_n_state(fsg), sizeof(fsg_pnode_t *)); lextree->ctx = ctx; lextree->dict = dict; lextree->d2p = d2p; lextree->mdef = mdef; lextree->wip = wip; lextree->pip = pip; /* Compute lc and rc for fsg. */ fsg_lextree_lc_rc(lextree); /* Create lextree for each state, i.e. an HMM network that * represents words for all arcs exiting that state. Note that * for a dense grammar such as an N-gram model, this will * rapidly exhaust all available memory. */ lextree->n_pnode = 0; n_leaves = 0; for (s = 0; s < fsg_model_n_state(fsg); s++) { lextree->root[s] = fsg_psubtree_init(lextree, fsg, s, &(lextree->alloc_head[s])); for (pn = lextree->alloc_head[s]; pn; pn = pn->alloc_next) { lextree->n_pnode++; if (pn->leaf) ++n_leaves; } } E_INFO("%d HMM nodes in lextree (%d leaves)\n", lextree->n_pnode, n_leaves); E_INFO("Allocated %d bytes (%d KiB) for all lextree nodes\n", lextree->n_pnode * sizeof(fsg_pnode_t), lextree->n_pnode * sizeof(fsg_pnode_t) / 1024); E_INFO("Allocated %d bytes (%d KiB) for lextree leafnodes\n", n_leaves * sizeof(fsg_pnode_t), n_leaves * sizeof(fsg_pnode_t) / 1024); #if __FSG_DBG__ fsg_lextree_dump(lextree, stdout); #endif return lextree; } void fsg_lextree_dump(fsg_lextree_t * lextree, FILE * fp) { int32 s; for (s = 0; s < fsg_model_n_state(lextree->fsg); s++) { fprintf(fp, "State %5d root %p\n", s, lextree->root[s]); fsg_psubtree_dump(lextree, lextree->root[s], fp); } fflush(fp); } void fsg_lextree_free(fsg_lextree_t * lextree) { int32 s; if (lextree == NULL) return; if (lextree->fsg) for (s = 0; s < fsg_model_n_state(lextree->fsg); s++) fsg_psubtree_free(lextree->alloc_head[s]); ckd_free_2d(lextree->lc); ckd_free_2d(lextree->rc); ckd_free(lextree->root); ckd_free(lextree->alloc_head); ckd_free(lextree); } /****************************** * psubtree stuff starts here * ******************************/ void fsg_glist_linklist_free(fsg_glist_linklist_t *glist) { if (glist) { fsg_glist_linklist_t *nxtglist; if (glist->glist) glist_free(glist->glist); nxtglist = glist->next; while (nxtglist) { ckd_free(glist); glist = nxtglist; if (glist->glist) glist_free(glist->glist); nxtglist = glist->next; } ckd_free(glist); } return; } void fsg_pnode_add_all_ctxt(fsg_pnode_ctxt_t * ctxt) { int32 i; for (i = 0; i < FSG_PNODE_CTXT_BVSZ; i++) ctxt->bv[i] = 0xffffffff; } uint32 fsg_pnode_ctxt_sub_generic(fsg_pnode_ctxt_t *src, fsg_pnode_ctxt_t *sub) { int32 i; uint32 res = 0; for (i = 0; i < FSG_PNODE_CTXT_BVSZ; i++) res |= (src->bv[i] = ~(sub->bv[i]) & src->bv[i]); return res; } /* * fsg_pnode_ctxt_sub(fsg_pnode_ctxt_t * src, fsg_pnode_ctxt_t * sub) * This has been moved into a macro in fsg_psubtree.h * because it is called so frequently! */ /* * Add the word emitted by the given transition (fsglink) to the given lextree * (rooted at root), and return the new lextree root. (There may actually be * several root nodes, maintained in a linked list via fsg_pnode_t.sibling. * "root" is the head of this list.) * lclist, rclist: sets of left and right context phones for this link. * alloc_head: head of a linear list of all allocated pnodes for the parent * FSG state, kept elsewhere and updated by this routine. */ static fsg_pnode_t * psubtree_add_trans(fsg_lextree_t *lextree, fsg_pnode_t * root, fsg_glist_linklist_t **curglist, fsg_link_t * fsglink, int16 *lclist, int16 *rclist, fsg_pnode_t ** alloc_head) { int32 silcipid; /* Silence CI phone ID */ int32 pronlen; /* Pronunciation length */ int32 wid; /* FSG (not dictionary!!) word ID */ int32 dictwid; /* Dictionary (not FSG!!) word ID */ int32 ssid; /* Senone Sequence ID */ int32 tmatid; gnode_t *gn; fsg_pnode_t *pnode, *pred, *head; int32 n_ci, p, lc, rc; glist_t lc_pnodelist; /* Temp pnodes list for different left contexts */ glist_t rc_pnodelist; /* Temp pnodes list for different right contexts */ int32 i, j; int n_lc_alloc = 0, n_int_alloc = 0, n_rc_alloc = 0; silcipid = bin_mdef_silphone(lextree->mdef); n_ci = bin_mdef_n_ciphone(lextree->mdef); wid = fsg_link_wid(fsglink); assert(wid >= 0); /* Cannot be a null transition */ dictwid = dict_wordid(lextree->dict, fsg_model_word_str(lextree->fsg, wid)); pronlen = dict_pronlen(lextree->dict, dictwid); assert(pronlen >= 1); assert(lclist[0] >= 0); /* At least one phonetic context provided */ assert(rclist[0] >= 0); head = *alloc_head; pred = NULL; if (pronlen == 1) { /* Single-phone word */ int ci = dict_first_phone(lextree->dict, dictwid); /* Only non-filler words are mpx */ if (!dict_filler_word(lextree->dict, dictwid)) { /* * Left diphone ID for single-phone words already assumes SIL is right * context; only left contexts need to be handled. */ lc_pnodelist = NULL; for (i = 0; lclist[i] >= 0; i++) { lc = lclist[i]; ssid = dict2pid_lrdiph_rc(lextree->d2p, ci, lc, silcipid); tmatid = bin_mdef_pid2tmatid(lextree->mdef, dict_first_phone(lextree->dict, dictwid)); /* Check if this ssid already allocated for some other context */ for (gn = lc_pnodelist; gn; gn = gnode_next(gn)) { pnode = (fsg_pnode_t *) gnode_ptr(gn); if (hmm_nonmpx_ssid(&pnode->hmm) == ssid) { /* already allocated; share it for this context phone */ fsg_pnode_add_ctxt(pnode, lc); break; } } if (!gn) { /* ssid not already allocated */ pnode = (fsg_pnode_t *) ckd_calloc(1, sizeof(fsg_pnode_t)); pnode->ctx = lextree->ctx; pnode->next.fsglink = fsglink; pnode->logs2prob = (fsg_link_logs2prob(fsglink) >> SENSCR_SHIFT) + lextree->wip + lextree->pip; pnode->ci_ext = dict_first_phone(lextree->dict, dictwid); pnode->ppos = 0; pnode->leaf = TRUE; pnode->sibling = root; /* All root nodes linked together */ fsg_pnode_add_ctxt(pnode, lc); /* Initially zeroed by calloc above */ pnode->alloc_next = head; head = pnode; root = pnode; ++n_lc_alloc; hmm_init(lextree->ctx, &pnode->hmm, FALSE, ssid, tmatid); lc_pnodelist = glist_add_ptr(lc_pnodelist, (void *) pnode); } } glist_free(lc_pnodelist); } else { /* Filler word; no context modelled */ ssid = bin_mdef_pid2ssid(lextree->mdef, ci); /* probably the same... */ tmatid = bin_mdef_pid2tmatid(lextree->mdef, ci); pnode = (fsg_pnode_t *) ckd_calloc(1, sizeof(fsg_pnode_t)); pnode->ctx = lextree->ctx; pnode->next.fsglink = fsglink; pnode->logs2prob = (fsg_link_logs2prob(fsglink) >> SENSCR_SHIFT) + lextree->wip + lextree->pip; pnode->ci_ext = silcipid; /* Presents SIL as context to neighbors */ pnode->ppos = 0; pnode->leaf = TRUE; pnode->sibling = root; fsg_pnode_add_all_ctxt(&(pnode->ctxt)); pnode->alloc_next = head; head = pnode; root = pnode; ++n_int_alloc; hmm_init(lextree->ctx, &pnode->hmm, FALSE, ssid, tmatid); } } else { /* Multi-phone word */ fsg_pnode_t **ssid_pnode_map; /* Temp array of ssid->pnode mapping */ ssid_pnode_map = (fsg_pnode_t **) ckd_calloc(n_ci, sizeof(fsg_pnode_t *)); lc_pnodelist = NULL; rc_pnodelist = NULL; for (p = 0; p < pronlen; p++) { int ci = dict_pron(lextree->dict, dictwid, p); if (p == 0) { /* Root phone, handle required left contexts */ /* Find if we already have an lc_pnodelist for the first phone of this word */ fsg_glist_linklist_t *glist; rc = dict_pron(lextree->dict, dictwid, 1); for (glist = *curglist; glist && glist->glist; glist = glist->next) { if (glist->ci == ci && glist->rc == rc) break; } if (glist && glist->glist) { assert(glist->ci == ci && glist->rc == rc); /* We've found a valid glist. Hook to it and move to next phoneme */ E_DEBUG(2,("Found match for (%d,%d)\n", ci, rc)); lc_pnodelist = glist->glist; /* Set the predecessor node for the future tree first */ pred = (fsg_pnode_t *) gnode_ptr(lc_pnodelist); continue; } else { /* Two cases that can bring us here * a. glist == NULL, i.e. end of current list. Create new entry. * b. glist->glist == NULL, i.e. first entry into list. */ if (glist == NULL) { /* Case a; reduce it to case b by allocing glist */ glist = (fsg_glist_linklist_t*) ckd_calloc(1, sizeof(fsg_glist_linklist_t)); glist->next = *curglist; *curglist = glist; } glist->ci = ci; glist->rc = rc; lc_pnodelist = glist->glist = NULL; /* Gets created below */ } for (i = 0; lclist[i] >= 0; i++) { lc = lclist[i]; ssid = dict2pid_ldiph_lc(lextree->d2p, ci, rc, lc); tmatid = bin_mdef_pid2tmatid(lextree->mdef, dict_first_phone(lextree->dict, dictwid)); /* Compression is not done by d2p, so we do it * here. This might be slow, but it might not * be... we'll see. */ pnode = ssid_pnode_map[0]; for (j = 0; j < n_ci && ssid_pnode_map[j] != NULL; ++j) { pnode = ssid_pnode_map[j]; if (hmm_nonmpx_ssid(&pnode->hmm) == ssid) break; } assert(j < n_ci); if (!pnode) { /* Allocate pnode for this new ssid */ pnode = (fsg_pnode_t *) ckd_calloc(1, sizeof (fsg_pnode_t)); pnode->ctx = lextree->ctx; /* This bit is tricky! For now we'll put the prob in the final link only */ /* pnode->logs2prob = (fsg_link_logs2prob(fsglink) >> SENSCR_SHIFT) + lextree->wip + lextree->pip; */ pnode->logs2prob = lextree->wip + lextree->pip; pnode->ci_ext = dict_first_phone(lextree->dict, dictwid); pnode->ppos = 0; pnode->leaf = FALSE; pnode->sibling = root; /* All root nodes linked together */ pnode->alloc_next = head; head = pnode; root = pnode; ++n_lc_alloc; hmm_init(lextree->ctx, &pnode->hmm, FALSE, ssid, tmatid); lc_pnodelist = glist_add_ptr(lc_pnodelist, (void *) pnode); ssid_pnode_map[j] = pnode; } fsg_pnode_add_ctxt(pnode, lc); } /* Put the lc_pnodelist back into glist */ glist->glist = lc_pnodelist; /* The predecessor node for the future tree is the root */ pred = root; } else if (p != pronlen - 1) { /* Word internal phone */ fsg_pnode_t *pnodeyoungest; ssid = dict2pid_internal(lextree->d2p, dictwid, p); tmatid = bin_mdef_pid2tmatid(lextree->mdef, dict_pron (lextree->dict, dictwid, p)); /* First check if we already have this ssid in our tree */ pnode = pred->next.succ; pnodeyoungest = pnode; /* The youngest sibling */ while (pnode && (hmm_nonmpx_ssid(&pnode->hmm) != ssid || pnode->leaf)) { pnode = pnode->sibling; } if (pnode && (hmm_nonmpx_ssid(&pnode->hmm) == ssid && !pnode->leaf)) { /* Found the ssid; go to next phoneme */ E_DEBUG(2,("Found match for %d\n", ci)); pred = pnode; continue; } /* pnode not found, allocate it */ pnode = (fsg_pnode_t *) ckd_calloc(1, sizeof(fsg_pnode_t)); pnode->ctx = lextree->ctx; pnode->logs2prob = lextree->pip; pnode->ci_ext = dict_pron(lextree->dict, dictwid, p); pnode->ppos = p; pnode->leaf = FALSE; pnode->sibling = pnodeyoungest; /* May be NULL */ if (p == 1) { /* Predecessor = set of root nodes for left ctxts */ for (gn = lc_pnodelist; gn; gn = gnode_next(gn)) { pred = (fsg_pnode_t *) gnode_ptr(gn); pred->next.succ = pnode; } } else { /* Predecessor = word internal node */ pred->next.succ = pnode; } pnode->alloc_next = head; head = pnode; ++n_int_alloc; hmm_init(lextree->ctx, &pnode->hmm, FALSE, ssid, tmatid); pred = pnode; } else { /* Leaf phone, handle required right contexts */ /* Note, leaf phones are not part of the tree */ xwdssid_t *rssid; memset((void *) ssid_pnode_map, 0, n_ci * sizeof(fsg_pnode_t *)); lc = dict_pron(lextree->dict, dictwid, p-1); rssid = dict2pid_rssid(lextree->d2p, ci, lc); tmatid = bin_mdef_pid2tmatid(lextree->mdef, dict_pron (lextree->dict, dictwid, p)); for (i = 0; rclist[i] >= 0; i++) { rc = rclist[i]; j = rssid->cimap[rc]; ssid = rssid->ssid[j]; pnode = ssid_pnode_map[j]; if (!pnode) { /* Allocate pnode for this new ssid */ pnode = (fsg_pnode_t *) ckd_calloc(1, sizeof (fsg_pnode_t)); pnode->ctx = lextree->ctx; /* We are plugging the word prob here. Ugly */ /* pnode->logs2prob = lextree->pip; */ pnode->logs2prob = (fsg_link_logs2prob(fsglink) >> SENSCR_SHIFT) + lextree->pip; pnode->ci_ext = dict_pron(lextree->dict, dictwid, p); pnode->ppos = p; pnode->leaf = TRUE; pnode->sibling = rc_pnodelist ? (fsg_pnode_t *) gnode_ptr(rc_pnodelist) : NULL; pnode->next.fsglink = fsglink; pnode->alloc_next = head; head = pnode; ++n_rc_alloc; hmm_init(lextree->ctx, &pnode->hmm, FALSE, ssid, tmatid); rc_pnodelist = glist_add_ptr(rc_pnodelist, (void *) pnode); ssid_pnode_map[j] = pnode; } else { assert(hmm_nonmpx_ssid(&pnode->hmm) == ssid); } fsg_pnode_add_ctxt(pnode, rc); } if (p == 1) { /* Predecessor = set of root nodes for left ctxts */ for (gn = lc_pnodelist; gn; gn = gnode_next(gn)) { pred = (fsg_pnode_t *) gnode_ptr(gn); if (!pred->next.succ) pred->next.succ = (fsg_pnode_t *) gnode_ptr(rc_pnodelist); else { /* Link to the end of the sibling chain */ fsg_pnode_t *succ = pred->next.succ; while (succ->sibling) succ = succ->sibling; succ->sibling = (fsg_pnode_t*) gnode_ptr(rc_pnodelist); /* Since all entries of lc_pnodelist point to the same array, sufficient to update it once */ break; } } } else { /* Predecessor = word internal node */ if (!pred->next.succ) pred->next.succ = (fsg_pnode_t *) gnode_ptr(rc_pnodelist); else { /* Link to the end of the sibling chain */ fsg_pnode_t *succ = pred->next.succ; while (succ->sibling) succ = succ->sibling; succ->sibling = (fsg_pnode_t *) gnode_ptr(rc_pnodelist); } } } } ckd_free((void *) ssid_pnode_map); /* glist_free(lc_pnodelist); Nope; this gets freed outside */ glist_free(rc_pnodelist); } E_DEBUG(2,("Allocated %d HMMs (%d lc, %d rc, %d internal)\n", n_lc_alloc + n_rc_alloc + n_int_alloc, n_lc_alloc, n_rc_alloc, n_int_alloc)); *alloc_head = head; return root; } static fsg_pnode_t * fsg_psubtree_init(fsg_lextree_t *lextree, fsg_model_t * fsg, int32 from_state, fsg_pnode_t ** alloc_head) { fsg_arciter_t *itor; fsg_link_t *fsglink; fsg_pnode_t *root; int32 n_ci, n_arc; fsg_glist_linklist_t *glist = NULL; root = NULL; assert(*alloc_head == NULL); n_ci = bin_mdef_n_ciphone(lextree->mdef); if (n_ci > (FSG_PNODE_CTXT_BVSZ * 32)) { E_FATAL ("#phones > %d; increase FSG_PNODE_CTXT_BVSZ and recompile\n", FSG_PNODE_CTXT_BVSZ * 32); } n_arc = 0; for (itor = fsg_model_arcs(fsg, from_state); itor; itor = fsg_arciter_next(itor)) { int32 dst; fsglink = fsg_arciter_get(itor); dst = fsglink->to_state; if (fsg_link_wid(fsglink) < 0) continue; E_DEBUG(2,("Building lextree for arc from %d to %d: %s\n", from_state, dst, fsg_model_word_str(fsg, fsg_link_wid(fsglink)))); root = psubtree_add_trans(lextree, root, &glist, fsglink, lextree->lc[from_state], lextree->rc[dst], alloc_head); ++n_arc; } E_DEBUG(2,("State %d has %d outgoing arcs\n", from_state, n_arc)); fsg_glist_linklist_free(glist); return root; } static void fsg_psubtree_free(fsg_pnode_t * head) { fsg_pnode_t *next; while (head) { next = head->alloc_next; hmm_deinit(&head->hmm); ckd_free(head); head = next; } } void fsg_psubtree_dump_node(fsg_lextree_t *tree, fsg_pnode_t *node, FILE *fp) { int32 i; fsg_link_t *tl; /* Indentation */ for (i = 0; i <= node->ppos; i++) fprintf(fp, " "); fprintf(fp, "%p.@", node); /* Pointer used as node * ID */ fprintf(fp, " %5d.SS", hmm_nonmpx_ssid(&node->hmm)); fprintf(fp, " %10d.LP", node->logs2prob); fprintf(fp, " %p.SIB", node->sibling); fprintf(fp, " %s.%d", bin_mdef_ciphone_str(tree->mdef, node->ci_ext), node->ppos); if ((node->ppos == 0) || node->leaf) { fprintf(fp, " ["); for (i = 0; i < FSG_PNODE_CTXT_BVSZ; i++) fprintf(fp, "%08x", node->ctxt.bv[i]); fprintf(fp, "]"); } if (node->leaf) { tl = node->next.fsglink; fprintf(fp, " {%s[%d->%d](%d)}", fsg_model_word_str(tree->fsg, tl->wid), tl->from_state, tl->to_state, tl->logs2prob); } else { fprintf(fp, " %p.NXT", node->next.succ); } fprintf(fp, "\n"); return; } void fsg_psubtree_dump(fsg_lextree_t *tree, fsg_pnode_t *root, FILE * fp) { fsg_pnode_t *succ; if (root == NULL) return; if (root->ppos == 0) { while(root->sibling && root->sibling->next.succ == root->next.succ) { fsg_psubtree_dump_node(tree, root, fp); root = root->sibling; } fflush(fp); } fsg_psubtree_dump_node(tree, root, fp); if (root->leaf) { if (root->ppos == 0 && root->sibling) { /* For single-phone words */ fsg_psubtree_dump(tree, root->sibling,fp); } return; } succ = root->next.succ; while(succ) { fsg_psubtree_dump(tree, succ,fp); succ = succ->sibling; } if (root->ppos == 0) { fsg_psubtree_dump(tree, root->sibling,fp); fflush(fp); } return; } void fsg_psubtree_pnode_deactivate(fsg_pnode_t * pnode) { hmm_clear(&pnode->hmm); }