/** * Find PHMM node with same senone sequence and tmat id as the given triphone. * Return ptr to PHMM node if found, NULL otherwise. */ static phmm_t * phmm_lookup(allphone_search_t * allphs, s3pid_t pid) { phmm_t *p; bin_mdef_t *mdef; phmm_t **ci_phmm; mdef = ((ps_search_t *) allphs)->acmod->mdef; ci_phmm = allphs->ci_phmm; for (p = ci_phmm[bin_mdef_pid2ci(mdef, pid)]; p; p = p->next) { if (mdef_pid2tmatid(mdef, p->pid) == mdef_pid2tmatid(mdef, pid)) if (mdef_pid2ssid(mdef, p->pid) == mdef_pid2ssid(mdef, pid)) return p; } //not found return NULL; }
/** * Build net from phone HMMs */ static int phmm_build(allphone_search_t * allphs) { phmm_t *p, **pid2phmm; bin_mdef_t *mdef; int32 lrc_size; uint32 *lc, *rc; s3pid_t pid; s3cipid_t ci; s3cipid_t *filler; int n_phmm, n_link; int i, nphone; mdef = ((ps_search_t *) allphs)->acmod->mdef; allphs->ci_phmm = (phmm_t **) ckd_calloc(bin_mdef_n_ciphone(mdef), sizeof(phmm_t *)); pid2phmm = (phmm_t **) ckd_calloc(bin_mdef_n_phone(mdef), sizeof(phmm_t *)); /* For each unique ciphone/triphone entry in mdef, create a PHMM node */ n_phmm = 0; nphone = allphs->ci_only ? bin_mdef_n_ciphone(mdef) : bin_mdef_n_phone(mdef); E_INFO("Building PHMM net of %d phones\n", nphone); for (pid = 0; pid < nphone; pid++) { if ((p = phmm_lookup(allphs, pid)) == NULL) { //not found, should be created p = (phmm_t *) ckd_calloc(1, sizeof(*p)); hmm_init(allphs->hmmctx, &(p->hmm), FALSE, mdef_pid2ssid(mdef, pid), mdef->phone[pid].tmat); p->pid = pid; p->ci = bin_mdef_pid2ci(mdef, pid); p->succlist = NULL; p->next = allphs->ci_phmm[p->ci]; allphs->ci_phmm[p->ci] = p; n_phmm++; } pid2phmm[pid] = p; } /* Fill out bitvecs of each PHMM node, alloc continuous memory chunk for context bitvectors */ lrc_size = bitvec_size(bin_mdef_n_ciphone(mdef)); lc = ckd_calloc(n_phmm * 2 * lrc_size, sizeof(bitvec_t)); rc = lc + (n_phmm * lrc_size); for (ci = 0; ci < mdef->n_ciphone; ci++) { for (p = allphs->ci_phmm[ci]; p; p = p->next) { p->lc = lc; lc += lrc_size; p->rc = rc; rc += lrc_size; } } /* Fill out lc and rc bitmaps (remember to map all fillers to each other!!) */ filler = (s3cipid_t *) ckd_calloc(bin_mdef_n_ciphone(mdef) + 1, sizeof(s3cipid_t)); /* Connect fillers */ i = 0; for (ci = 0; ci < bin_mdef_n_ciphone(mdef); ci++) { p = pid2phmm[ci]; bitvec_set_all(p->lc, bin_mdef_n_ciphone(mdef)); bitvec_set_all(p->rc, bin_mdef_n_ciphone(mdef)); if (mdef->phone[ci].info.ci.filler) { filler[i++] = ci; } } filler[i] = BAD_S3CIPID; /* Loop over cdphones only if ci_only is not set */ for (pid = bin_mdef_n_ciphone(mdef); pid < nphone; pid++) { p = pid2phmm[pid]; if (mdef->phone[mdef->phone[pid].info.cd.ctx[1]].info.ci.filler) { for (i = 0; IS_S3CIPID(filler[i]); i++) bitvec_set(p->lc, filler[i]); } else bitvec_set(p->lc, mdef->phone[pid].info.cd.ctx[1]); if (mdef->phone[mdef->phone[pid].info.cd.ctx[2]].info.ci.filler) { for (i = 0; IS_S3CIPID(filler[i]); i++) bitvec_set(p->rc, filler[i]); } else bitvec_set(p->rc, mdef->phone[pid].info.cd.ctx[2]); } ckd_free(pid2phmm); ckd_free(filler); /* Create links between PHMM nodes */ n_link = phmm_link(allphs); E_INFO("%d nodes, %d links\n", n_phmm, n_link); return 0; }
bin_mdef_t * bin_mdef_read(cmd_ln_t *config, const char *filename) { bin_mdef_t *m; FILE *fh; size_t tree_start; int32 val, i, swap, pos, end; int32 *sseq_size; int do_mmap; /* Try to read it as text first. */ if ((m = bin_mdef_read_text(config, filename)) != NULL) return m; E_INFO("Reading binary model definition: %s\n", filename); if ((fh = fopen(filename, "rb")) == NULL) return NULL; if (fread(&val, 4, 1, fh) != 1) { fclose(fh); E_ERROR_SYSTEM("Failed to read byte-order marker from %s\n", filename); return NULL; } swap = 0; if (val == BIN_MDEF_OTHER_ENDIAN) { swap = 1; E_INFO("Must byte-swap %s\n", filename); } if (fread(&val, 4, 1, fh) != 1) { fclose(fh); E_ERROR_SYSTEM("Failed to read version from %s\n", filename); return NULL; } if (swap) SWAP_INT32(&val); if (val > BIN_MDEF_FORMAT_VERSION) { E_ERROR("File format version %d for %s is newer than library\n", val, filename); fclose(fh); return NULL; } if (fread(&val, 4, 1, fh) != 1) { fclose(fh); E_ERROR_SYSTEM("Failed to read header length from %s\n", filename); return NULL; } if (swap) SWAP_INT32(&val); /* Skip format descriptor. */ fseek(fh, val, SEEK_CUR); /* Finally allocate it. */ m = ckd_calloc(1, sizeof(*m)); m->refcnt = 1; /* Check these, to make gcc/glibc shut up. */ #define FREAD_SWAP32_CHK(dest) \ if (fread((dest), 4, 1, fh) != 1) { \ fclose(fh); \ ckd_free(m); \ E_ERROR_SYSTEM("Failed to read %s from %s\n", #dest, filename); \ return NULL; \ } \ if (swap) SWAP_INT32(dest); FREAD_SWAP32_CHK(&m->n_ciphone); FREAD_SWAP32_CHK(&m->n_phone); FREAD_SWAP32_CHK(&m->n_emit_state); FREAD_SWAP32_CHK(&m->n_ci_sen); FREAD_SWAP32_CHK(&m->n_sen); FREAD_SWAP32_CHK(&m->n_tmat); FREAD_SWAP32_CHK(&m->n_sseq); FREAD_SWAP32_CHK(&m->n_ctx); FREAD_SWAP32_CHK(&m->n_cd_tree); FREAD_SWAP32_CHK(&m->sil); /* CI names are first in the file. */ m->ciname = ckd_calloc(m->n_ciphone, sizeof(*m->ciname)); /* Decide whether to read in the whole file or mmap it. */ do_mmap = config ? cmd_ln_boolean_r(config, "-mmap") : TRUE; if (swap) { E_WARN("-mmap specified, but mdef is other-endian. Will not memory-map.\n"); do_mmap = FALSE; } /* Actually try to mmap it. */ if (do_mmap) { m->filemap = mmio_file_read(filename); if (m->filemap == NULL) do_mmap = FALSE; } pos = ftell(fh); if (do_mmap) { /* Get the base pointer from the memory map. */ m->ciname[0] = (char *)mmio_file_ptr(m->filemap) + pos; /* Success! */ m->alloc_mode = BIN_MDEF_ON_DISK; } else { /* Read everything into memory. */ m->alloc_mode = BIN_MDEF_IN_MEMORY; fseek(fh, 0, SEEK_END); end = ftell(fh); fseek(fh, pos, SEEK_SET); m->ciname[0] = ckd_malloc(end - pos); if (fread(m->ciname[0], 1, end - pos, fh) != end - pos) E_FATAL("Failed to read %d bytes of data from %s\n", end - pos, filename); } for (i = 1; i < m->n_ciphone; ++i) m->ciname[i] = m->ciname[i - 1] + strlen(m->ciname[i - 1]) + 1; /* Skip past the padding. */ tree_start = m->ciname[i - 1] + strlen(m->ciname[i - 1]) + 1 - m->ciname[0]; tree_start = (tree_start + 3) & ~3; m->cd_tree = (cd_tree_t *) (m->ciname[0] + tree_start); if (swap) { for (i = 0; i < m->n_cd_tree; ++i) { SWAP_INT16(&m->cd_tree[i].ctx); SWAP_INT16(&m->cd_tree[i].n_down); SWAP_INT32(&m->cd_tree[i].c.down); } } m->phone = (mdef_entry_t *) (m->cd_tree + m->n_cd_tree); if (swap) { for (i = 0; i < m->n_phone; ++i) { SWAP_INT32(&m->phone[i].ssid); SWAP_INT32(&m->phone[i].tmat); } } sseq_size = (int32 *) (m->phone + m->n_phone); if (swap) SWAP_INT32(sseq_size); m->sseq = ckd_calloc(m->n_sseq, sizeof(*m->sseq)); m->sseq[0] = (uint16 *) (sseq_size + 1); if (swap) { for (i = 0; i < *sseq_size; ++i) SWAP_INT16(m->sseq[0] + i); } if (m->n_emit_state) { for (i = 1; i < m->n_sseq; ++i) m->sseq[i] = m->sseq[0] + i * m->n_emit_state; } else { m->sseq_len = (uint8 *) (m->sseq[0] + *sseq_size); for (i = 1; i < m->n_sseq; ++i) m->sseq[i] = m->sseq[i - 1] + m->sseq_len[i - 1]; } /* Now build the CD-to-CI mappings using the senone sequences. * This is the only really accurate way to do it, though it is * still inaccurate in the case of heterogeneous topologies or * cross-state tying. */ m->cd2cisen = (int16 *) ckd_malloc(m->n_sen * sizeof(*m->cd2cisen)); m->sen2cimap = (int16 *) ckd_malloc(m->n_sen * sizeof(*m->sen2cimap)); /* Default mappings (identity, none) */ for (i = 0; i < m->n_ci_sen; ++i) m->cd2cisen[i] = i; for (; i < m->n_sen; ++i) m->cd2cisen[i] = -1; for (i = 0; i < m->n_sen; ++i) m->sen2cimap[i] = -1; for (i = 0; i < m->n_phone; ++i) { int32 j, ssid = m->phone[i].ssid; for (j = 0; j < bin_mdef_n_emit_state_phone(m, i); ++j) { int s = bin_mdef_sseq2sen(m, ssid, j); int ci = bin_mdef_pid2ci(m, i); /* Take the first one and warn if we have cross-state tying. */ if (m->sen2cimap[s] == -1) m->sen2cimap[s] = ci; if (m->sen2cimap[s] != ci) E_WARN ("Senone %d is shared between multiple base phones\n", s); if (j > bin_mdef_n_emit_state_phone(m, ci)) E_WARN("CD phone %d has fewer states than CI phone %d\n", i, ci); else m->cd2cisen[s] = bin_mdef_sseq2sen(m, m->phone[ci].ssid, j); } } /* Set the silence phone. */ m->sil = bin_mdef_ciphone_id(m, S3_SILENCE_CIPHONE); E_INFO ("%d CI-phone, %d CD-phone, %d emitstate/phone, %d CI-sen, %d Sen, %d Sen-Seq\n", m->n_ciphone, m->n_phone - m->n_ciphone, m->n_emit_state, m->n_ci_sen, m->n_sen, m->n_sseq); fclose(fh); return m; }