/* -*- c-basic-offset:4; indent-tabs-mode: nil -*- */ /* ==================================================================== * Copyright (c) 1999-2004 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 work was supported in part by funding from the Defense Advanced * Research Projects Agency and the National Science Foundation of the * United States of America, and the CMU Sphinx Speech Consortium. * * 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. * * ==================================================================== * */ #include #include "dict2pid.h" #include "hmm.h" /** * @file dict2pid.c - dictionary word to senone sequence mappings */ void compress_table(s3ssid_t * uncomp_tab, s3ssid_t * com_tab, s3cipid_t * ci_map, int32 n_ci) { int32 found; int32 r; int32 tmp_r; for (r = 0; r < n_ci; r++) { com_tab[r] = BAD_S3SSID; ci_map[r] = BAD_S3CIPID; } /** Compress this map */ for (r = 0; r < n_ci; r++) { found = 0; for (tmp_r = 0; tmp_r < r && com_tab[tmp_r] != BAD_S3SSID; tmp_r++) { /* If it appears before, just filled in cimap; */ if (uncomp_tab[r] == com_tab[tmp_r]) { found = 1; ci_map[r] = tmp_r; break; } } if (found == 0) { com_tab[tmp_r] = uncomp_tab[r]; ci_map[r] = tmp_r; } } } static void compress_right_context_tree(dict2pid_t * d2p, s3ssid_t ***rdiph_rc) { int32 n_ci; int32 b, l, r; s3ssid_t *rmap; s3ssid_t *tmpssid; s3cipid_t *tmpcimap; bin_mdef_t *mdef = d2p->mdef; size_t alloc; n_ci = mdef->n_ciphone; tmpssid = ckd_calloc(n_ci, sizeof(s3ssid_t)); tmpcimap = ckd_calloc(n_ci, sizeof(s3cipid_t)); d2p->rssid = (xwdssid_t **) ckd_calloc(mdef->n_ciphone, sizeof(xwdssid_t *)); alloc = mdef->n_ciphone * sizeof(xwdssid_t *); for (b = 0; b < n_ci; b++) { d2p->rssid[b] = (xwdssid_t *) ckd_calloc(mdef->n_ciphone, sizeof(xwdssid_t)); alloc += mdef->n_ciphone * sizeof(xwdssid_t); for (l = 0; l < n_ci; l++) { rmap = rdiph_rc[b][l]; compress_table(rmap, tmpssid, tmpcimap, mdef->n_ciphone); for (r = 0; r < mdef->n_ciphone && tmpssid[r] != BAD_S3SSID; r++); if (tmpssid[0] != BAD_S3SSID) { d2p->rssid[b][l].ssid = ckd_calloc(r, sizeof(s3ssid_t)); memcpy(d2p->rssid[b][l].ssid, tmpssid, r * sizeof(s3ssid_t)); d2p->rssid[b][l].cimap = ckd_calloc(mdef->n_ciphone, sizeof(s3cipid_t)); memcpy(d2p->rssid[b][l].cimap, tmpcimap, (mdef->n_ciphone) * sizeof(s3cipid_t)); d2p->rssid[b][l].n_ssid = r; } else { d2p->rssid[b][l].ssid = NULL; d2p->rssid[b][l].cimap = NULL; d2p->rssid[b][l].n_ssid = 0; } } } E_INFO("Allocated %d bytes (%d KiB) for word-final triphones\n", (int)alloc, (int)alloc / 1024); ckd_free(tmpssid); ckd_free(tmpcimap); } static void compress_left_right_context_tree(dict2pid_t * d2p) { int32 n_ci; int32 b, l, r; s3ssid_t *rmap; s3ssid_t *tmpssid; s3cipid_t *tmpcimap; bin_mdef_t *mdef = d2p->mdef; size_t alloc; n_ci = mdef->n_ciphone; tmpssid = ckd_calloc(n_ci, sizeof(s3ssid_t)); tmpcimap = ckd_calloc(n_ci, sizeof(s3cipid_t)); assert(d2p->lrdiph_rc); d2p->lrssid = (xwdssid_t **) ckd_calloc(mdef->n_ciphone, sizeof(xwdssid_t *)); alloc = mdef->n_ciphone * sizeof(xwdssid_t *); for (b = 0; b < n_ci; b++) { d2p->lrssid[b] = (xwdssid_t *) ckd_calloc(mdef->n_ciphone, sizeof(xwdssid_t)); alloc += mdef->n_ciphone * sizeof(xwdssid_t); for (l = 0; l < n_ci; l++) { rmap = d2p->lrdiph_rc[b][l]; compress_table(rmap, tmpssid, tmpcimap, mdef->n_ciphone); for (r = 0; r < mdef->n_ciphone && tmpssid[r] != BAD_S3SSID; r++); if (tmpssid[0] != BAD_S3SSID) { d2p->lrssid[b][l].ssid = ckd_calloc(r, sizeof(s3ssid_t)); memcpy(d2p->lrssid[b][l].ssid, tmpssid, r * sizeof(s3ssid_t)); d2p->lrssid[b][l].cimap = ckd_calloc(mdef->n_ciphone, sizeof(s3cipid_t)); memcpy(d2p->lrssid[b][l].cimap, tmpcimap, (mdef->n_ciphone) * sizeof(s3cipid_t)); d2p->lrssid[b][l].n_ssid = r; } else { d2p->lrssid[b][l].ssid = NULL; d2p->lrssid[b][l].cimap = NULL; d2p->lrssid[b][l].n_ssid = 0; } } } /* Try to compress lrdiph_rc into lrdiph_rc_compressed */ ckd_free(tmpssid); ckd_free(tmpcimap); E_INFO("Allocated %d bytes (%d KiB) for single-phone word triphones\n", (int)alloc, (int)alloc / 1024); } /** ARCHAN, A duplicate of get_rc_npid in ctxt_table.h. I doubt whether it is correct because the compressed map has not been checked. */ int32 get_rc_nssid(dict2pid_t * d2p, s3wid_t w) { int32 pronlen; s3cipid_t b, lc; dict_t *dict = d2p->dict; pronlen = dict->word[w].pronlen; b = dict->word[w].ciphone[pronlen - 1]; if (pronlen == 1) { /* Is this true ? No known left context. But all cimaps (for any l) are identical; pick one */ /*E_INFO("Single phone word\n"); */ return (d2p->lrssid[b][0].n_ssid); } else { /* E_INFO("Multiple phone word\n"); */ lc = dict->word[w].ciphone[pronlen - 2]; return (d2p->rssid[b][lc].n_ssid); } } s3cipid_t * dict2pid_get_rcmap(dict2pid_t * d2p, s3wid_t w) { int32 pronlen; s3cipid_t b, lc; dict_t *dict = d2p->dict; pronlen = dict->word[w].pronlen; b = dict->word[w].ciphone[pronlen - 1]; if (pronlen == 1) { /* Is this true ? No known left context. But all cimaps (for any l) are identical; pick one */ /*E_INFO("Single phone word\n"); */ return (d2p->lrssid[b][0].cimap); } else { /* E_INFO("Multiple phone word\n"); */ lc = dict->word[w].ciphone[pronlen - 2]; return (d2p->rssid[b][lc].cimap); } } static void free_compress_map(xwdssid_t ** tree, int32 n_ci) { int32 b, l; for (b = 0; b < n_ci; b++) { for (l = 0; l < n_ci; l++) { ckd_free(tree[b][l].ssid); ckd_free(tree[b][l].cimap); } ckd_free(tree[b]); } ckd_free(tree); } static void populate_lrdiph(dict2pid_t *d2p, s3ssid_t ***rdiph_rc, s3cipid_t b) { bin_mdef_t *mdef = d2p->mdef; s3cipid_t l, r; for (l = 0; l < bin_mdef_n_ciphone(mdef); l++) { for (r = 0; r < bin_mdef_n_ciphone(mdef); r++) { s3pid_t p; p = bin_mdef_phone_id_nearest(mdef, (s3cipid_t) b, (s3cipid_t) l, (s3cipid_t) r, WORD_POSN_SINGLE); d2p->lrdiph_rc[b][l][r] = bin_mdef_pid2ssid(mdef, p); if (r == bin_mdef_silphone(mdef)) d2p->ldiph_lc[b][r][l] = bin_mdef_pid2ssid(mdef, p); if (rdiph_rc && l == bin_mdef_silphone(mdef)) rdiph_rc[b][l][r] = bin_mdef_pid2ssid(mdef, p); assert(IS_S3SSID(bin_mdef_pid2ssid(mdef, p))); E_DEBUG(2,("%s(%s,%s) => %d / %d\n", bin_mdef_ciphone_str(mdef, b), bin_mdef_ciphone_str(mdef, l), bin_mdef_ciphone_str(mdef, r), p, bin_mdef_pid2ssid(mdef, p))); } } } int dict2pid_add_word(dict2pid_t *d2p, int32 wid) { bin_mdef_t *mdef = d2p->mdef; dict_t *d = d2p->dict; if (dict_pronlen(d, wid) > 1) { s3cipid_t l; /* Make sure we have left and right context diphones for this * word. */ if (d2p->ldiph_lc[dict_first_phone(d, wid)][dict_second_phone(d, wid)][0] == BAD_S3SSID) { E_DEBUG(2, ("Filling in left-context diphones for %s(?,%s)\n", bin_mdef_ciphone_str(mdef, dict_first_phone(d, wid)), bin_mdef_ciphone_str(mdef, dict_second_phone(d, wid)))); for (l = 0; l < bin_mdef_n_ciphone(mdef); l++) { int p = bin_mdef_phone_id_nearest(mdef, dict_first_phone(d, wid), l, dict_second_phone(d, wid), WORD_POSN_BEGIN); d2p->ldiph_lc[dict_first_phone(d, wid)][dict_second_phone(d, wid)][l] = bin_mdef_pid2ssid(mdef, p); } } if (d2p->rssid[dict_last_phone(d, wid)][dict_second_last_phone(d, wid)].n_ssid == 0) { s3ssid_t *rmap; s3ssid_t *tmpssid; s3cipid_t *tmpcimap; s3cipid_t r; E_DEBUG(2, ("Filling in right-context diphones for %s(%s,?)\n", bin_mdef_ciphone_str(mdef, dict_last_phone(d, wid)), bin_mdef_ciphone_str(mdef, dict_second_last_phone(d, wid)))); rmap = ckd_calloc(bin_mdef_n_ciphone(mdef), sizeof(*rmap)); for (r = 0; r < bin_mdef_n_ciphone(mdef); r++) { int p = bin_mdef_phone_id_nearest(mdef, dict_last_phone(d, wid), dict_second_last_phone(d, wid), r, WORD_POSN_END); rmap[r] = bin_mdef_pid2ssid(mdef, p); } tmpssid = ckd_calloc(bin_mdef_n_ciphone(mdef), sizeof(*tmpssid)); tmpcimap = ckd_calloc(bin_mdef_n_ciphone(mdef), sizeof(*tmpcimap)); compress_table(rmap, tmpssid, tmpcimap, bin_mdef_n_ciphone(mdef)); for (r = 0; r < mdef->n_ciphone && tmpssid[r] != BAD_S3SSID; r++) ; d2p->rssid[dict_last_phone(d, wid)][dict_second_last_phone(d, wid)].ssid = tmpssid; d2p->rssid[dict_last_phone(d, wid)][dict_second_last_phone(d, wid)].cimap = tmpcimap; d2p->rssid[dict_last_phone(d, wid)][dict_second_last_phone(d, wid)].n_ssid = r; ckd_free(rmap); } } else { /* Make sure we have a left-right context triphone entry for * this word. */ E_INFO("Filling in context triphones for %s(?,?)\n", bin_mdef_ciphone_str(mdef, dict_first_phone(d, wid))); if (d2p->lrdiph_rc[dict_first_phone(d, wid)][0][0] == BAD_S3SSID) { populate_lrdiph(d2p, NULL, dict_first_phone(d, wid)); } } return 0; } s3ssid_t dict2pid_internal(dict2pid_t *d2p, int32 wid, int pos) { int b, l, r, p; dict_t *dict = d2p->dict; bin_mdef_t *mdef = d2p->mdef; if (pos == 0 || pos == dict_pronlen(dict, wid)) return BAD_S3SSID; b = dict_pron(dict, wid, pos); l = dict_pron(dict, wid, pos - 1); r = dict_pron(dict, wid, pos + 1); p = bin_mdef_phone_id_nearest(mdef, (s3cipid_t) b, (s3cipid_t) l, (s3cipid_t) r, WORD_POSN_INTERNAL); return bin_mdef_pid2ssid(mdef, p); } dict2pid_t * dict2pid_build(bin_mdef_t * mdef, dict_t * dict) { dict2pid_t *dict2pid; s3ssid_t ***rdiph_rc; bitvec_t *ldiph, *rdiph, *single; int32 pronlen; int32 b, l, r, w, p; E_INFO("Building PID tables for dictionary\n"); assert(mdef); assert(dict); dict2pid = (dict2pid_t *) ckd_calloc(1, sizeof(dict2pid_t)); dict2pid->refcount = 1; dict2pid->mdef = bin_mdef_retain(mdef); dict2pid->dict = dict_retain(dict); E_INFO("Allocating %d^3 * %d bytes (%d KiB) for word-initial triphones\n", mdef->n_ciphone, sizeof(s3ssid_t), mdef->n_ciphone * mdef->n_ciphone * mdef->n_ciphone * sizeof(s3ssid_t) / 1024); dict2pid->ldiph_lc = (s3ssid_t ***) ckd_calloc_3d(mdef->n_ciphone, mdef->n_ciphone, mdef->n_ciphone, sizeof(s3ssid_t)); /* Only used internally to generate rssid */ rdiph_rc = (s3ssid_t ***) ckd_calloc_3d(mdef->n_ciphone, mdef->n_ciphone, mdef->n_ciphone, sizeof(s3ssid_t)); dict2pid->lrdiph_rc = (s3ssid_t ***) ckd_calloc_3d(mdef->n_ciphone, mdef->n_ciphone, mdef->n_ciphone, sizeof (s3ssid_t)); /* Actually could use memset for this, if BAD_S3SSID is guaranteed * to be 65535... */ for (b = 0; b < mdef->n_ciphone; ++b) { for (r = 0; r < mdef->n_ciphone; ++r) { for (l = 0; l < mdef->n_ciphone; ++l) { dict2pid->ldiph_lc[b][r][l] = BAD_S3SSID; dict2pid->lrdiph_rc[b][l][r] = BAD_S3SSID; rdiph_rc[b][l][r] = BAD_S3SSID; } } } /* Track which diphones / ciphones have been seen. */ ldiph = bitvec_alloc(mdef->n_ciphone * mdef->n_ciphone); rdiph = bitvec_alloc(mdef->n_ciphone * mdef->n_ciphone); single = bitvec_alloc(mdef->n_ciphone); for (w = 0; w < dict_size(dict2pid->dict); w++) { pronlen = dict_pronlen(dict, w); if (pronlen >= 2) { b = dict_first_phone(dict, w); r = dict_second_phone(dict, w); /* Populate ldiph_lc */ if (bitvec_is_clear(ldiph, b * mdef->n_ciphone + r)) { /* Mark this diphone as done */ bitvec_set(ldiph, b * mdef->n_ciphone + r); /* Record all possible ssids for b(?,r) */ for (l = 0; l < bin_mdef_n_ciphone(mdef); l++) { p = bin_mdef_phone_id_nearest(mdef, (s3cipid_t) b, (s3cipid_t) l, (s3cipid_t) r, WORD_POSN_BEGIN); dict2pid->ldiph_lc[b][r][l] = bin_mdef_pid2ssid(mdef, p); } } /* Populate rdiph_rc */ l = dict_second_last_phone(dict, w); b = dict_last_phone(dict, w); if (bitvec_is_clear(rdiph, b * mdef->n_ciphone + l)) { /* Mark this diphone as done */ bitvec_set(rdiph, b * mdef->n_ciphone + l); for (r = 0; r < bin_mdef_n_ciphone(mdef); r++) { p = bin_mdef_phone_id_nearest(mdef, (s3cipid_t) b, (s3cipid_t) l, (s3cipid_t) r, WORD_POSN_END); rdiph_rc[b][l][r] = bin_mdef_pid2ssid(mdef, p); } } } else if (pronlen == 1) { b = dict_pron(dict, w, 0); E_DEBUG(1,("Building tables for single phone word %s phone %d = %s\n", dict_wordstr(dict, w), b, bin_mdef_ciphone_str(mdef, b))); /* Populate lrdiph_rc (and also ldiph_lc, rdiph_rc if needed) */ if (bitvec_is_clear(single, b)) { populate_lrdiph(dict2pid, rdiph_rc, b); bitvec_set(single, b); } } } bitvec_free(ldiph); bitvec_free(rdiph); bitvec_free(single); /* Try to compress rdiph_rc into rdiph_rc_compressed */ compress_right_context_tree(dict2pid, rdiph_rc); compress_left_right_context_tree(dict2pid); ckd_free_3d(rdiph_rc); dict2pid_report(dict2pid); return dict2pid; } dict2pid_t * dict2pid_retain(dict2pid_t *d2p) { ++d2p->refcount; return d2p; } int dict2pid_free(dict2pid_t * d2p) { if (d2p == NULL) return 0; if (--d2p->refcount > 0) return d2p->refcount; if (d2p->ldiph_lc) ckd_free_3d((void ***) d2p->ldiph_lc); if (d2p->lrdiph_rc) ckd_free_3d((void ***) d2p->lrdiph_rc); if (d2p->rssid) free_compress_map(d2p->rssid, bin_mdef_n_ciphone(d2p->mdef)); if (d2p->lrssid) free_compress_map(d2p->lrssid, bin_mdef_n_ciphone(d2p->mdef)); bin_mdef_free(d2p->mdef); dict_free(d2p->dict); ckd_free(d2p); return 0; } void dict2pid_report(dict2pid_t * d2p) { } void dict2pid_dump(FILE * fp, dict2pid_t * d2p) { int32 w, p, pronlen; int32 i, j, b, l, r; bin_mdef_t *mdef = d2p->mdef; dict_t *dict = d2p->dict; fprintf(fp, "# INTERNAL (wd comssid ssid ssid ... ssid comssid)\n"); for (w = 0; w < dict_size(dict); w++) { fprintf(fp, "%30s ", dict_wordstr(dict, w)); pronlen = dict_pronlen(dict, w); for (p = 0; p < pronlen; p++) fprintf(fp, " %5d", dict2pid_internal(d2p, w, p)); fprintf(fp, "\n"); } fprintf(fp, "#\n"); fprintf(fp, "# LDIPH_LC (b r l ssid)\n"); for (b = 0; b < bin_mdef_n_ciphone(mdef); b++) { for (r = 0; r < bin_mdef_n_ciphone(mdef); r++) { for (l = 0; l < bin_mdef_n_ciphone(mdef); l++) { if (IS_S3SSID(d2p->ldiph_lc[b][r][l])) fprintf(fp, "%6s %6s %6s %5d\n", bin_mdef_ciphone_str(mdef, (s3cipid_t) b), bin_mdef_ciphone_str(mdef, (s3cipid_t) r), bin_mdef_ciphone_str(mdef, (s3cipid_t) l), d2p->ldiph_lc[b][r][l]); /* RAH, ldiph_lc is returning an int32, %d expects an int16 */ } } } fprintf(fp, "#\n"); fprintf(fp, "# SSEQ %d (senid senid ...)\n", mdef->n_sseq); for (i = 0; i < mdef->n_sseq; i++) { fprintf(fp, "%5d ", i); for (j = 0; j < bin_mdef_n_emit_state(mdef); j++) fprintf(fp, " %5d", mdef->sseq[i][j]); fprintf(fp, "\n"); } fprintf(fp, "#\n"); fprintf(fp, "# END\n"); fflush(fp); }