• Main Page
  • Related Pages
  • Data Structures
  • Files
  • File List
  • Globals

src/libpocketsphinx/bin_mdef.c

00001 /* -*- c-basic-offset: 4; indent-tabs-mode: nil -*- */
00002 /* ====================================================================
00003  * Copyright (c) 2005 Carnegie Mellon University.  All rights 
00004  * reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  *
00010  * 1. Redistributions of source code must retain the above copyright
00011  *    notice, this list of conditions and the following disclaimer. 
00012  *
00013  * 2. Redistributions in binary form must reproduce the above copyright
00014  *    notice, this list of conditions and the following disclaimer in
00015  *    the documentation and/or other materials provided with the
00016  *    distribution.
00017  *
00018  * This work was supported in part by funding from the Defense Advanced 
00019  * Research Projects Agency and the National Science Foundation of the 
00020  * United States of America, and the CMU Sphinx Speech Consortium.
00021  *
00022  * THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND 
00023  * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
00024  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00025  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY
00026  * NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00027  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
00028  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
00029  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
00030  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
00031  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
00032  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00033  *
00034  * ====================================================================
00035  *
00036  */
00037 /*********************************************************************
00038  *
00039  * File: bin_mdef.c
00040  * 
00041  * Description: 
00042  *      Binary format model definition files, with support for
00043  *      heterogeneous topologies and variable-size N-phones
00044  *
00045  * Author: 
00046  *      David Huggins-Daines <dhuggins@cs.cmu.edu>
00047  *********************************************************************/
00048 
00049 /* System headers. */
00050 #include <stdio.h>
00051 #include <string.h>
00052 #include <assert.h>
00053 
00054 /* SphinxBase headers. */
00055 #include <prim_type.h>
00056 #include <ckd_alloc.h>
00057 #include <byteorder.h>
00058 #include <case.h>
00059 #include <err.h>
00060 
00061 /* Local headers. */
00062 #include "mdef.h"
00063 #include "bin_mdef.h"
00064 
00065 bin_mdef_t *
00066 bin_mdef_read_text(cmd_ln_t *config, const char *filename)
00067 {
00068     bin_mdef_t *bmdef;
00069     mdef_t *mdef;
00070     int i, nodes, ci_idx, lc_idx, rc_idx;
00071     int nchars;
00072 
00073     if ((mdef = mdef_init((char *) filename, TRUE)) == NULL)
00074         return NULL;
00075 
00076     bmdef = ckd_calloc(1, sizeof(*bmdef));
00077 
00078     /* Easy stuff.  The mdef.c code has done the heavy lifting for us. */
00079     bmdef->n_ciphone = mdef->n_ciphone;
00080     bmdef->n_phone = mdef->n_phone;
00081     bmdef->n_emit_state = mdef->n_emit_state;
00082     bmdef->n_ci_sen = mdef->n_ci_sen;
00083     bmdef->n_sen = mdef->n_sen;
00084     bmdef->n_tmat = mdef->n_tmat;
00085     bmdef->n_sseq = mdef->n_sseq;
00086     bmdef->sseq = mdef->sseq;
00087     bmdef->cd2cisen = mdef->cd2cisen;
00088     bmdef->sen2cimap = mdef->sen2cimap;
00089     bmdef->n_ctx = 3;           /* Triphones only. */
00090     bmdef->sil = mdef->sil;
00091     mdef->sseq = NULL;          /* We are taking over this one. */
00092     mdef->cd2cisen = NULL;      /* And this one. */
00093     mdef->sen2cimap = NULL;     /* And this one. */
00094 
00095     /* Get the phone names.  If they are not sorted
00096      * ASCII-betically then we are in a world of hurt and
00097      * therefore will simply refuse to continue. */
00098     bmdef->ciname = ckd_calloc(bmdef->n_ciphone, sizeof(*bmdef->ciname));
00099     nchars = 0;
00100     for (i = 0; i < bmdef->n_ciphone; ++i)
00101         nchars += strlen(mdef->ciphone[i].name) + 1;
00102     bmdef->ciname[0] = ckd_calloc(nchars, 1);
00103     strcpy(bmdef->ciname[0], mdef->ciphone[0].name);
00104     for (i = 1; i < bmdef->n_ciphone; ++i) {
00105         bmdef->ciname[i] =
00106             bmdef->ciname[i - 1] + strlen(bmdef->ciname[i - 1]) + 1;
00107         strcpy(bmdef->ciname[i], mdef->ciphone[i].name);
00108         if (i > 0 && strcmp(bmdef->ciname[i - 1], bmdef->ciname[i]) > 0) {
00109             /* FIXME: there should be a solution to this, actually. */
00110             E_ERROR("Phone names are not in sorted order, sorry.");
00111             bin_mdef_free(bmdef);
00112             return NULL;
00113         }
00114     }
00115 
00116     /* Copy over phone information. */
00117     bmdef->phone = ckd_calloc(bmdef->n_phone, sizeof(*bmdef->phone));
00118     for (i = 0; i < mdef->n_phone; ++i) {
00119         bmdef->phone[i].ssid = mdef->phone[i].ssid;
00120         bmdef->phone[i].tmat = mdef->phone[i].tmat;
00121         if (i < bmdef->n_ciphone) {
00122             bmdef->phone[i].info.ci.filler = mdef->ciphone[i].filler;
00123         }
00124         else {
00125             bmdef->phone[i].info.cd.wpos = mdef->phone[i].wpos;
00126             bmdef->phone[i].info.cd.ctx[0] = (uint8)mdef->phone[i].ci;
00127             bmdef->phone[i].info.cd.ctx[1] = (uint8)mdef->phone[i].lc;
00128             bmdef->phone[i].info.cd.ctx[2] = (uint8)mdef->phone[i].rc;
00129         }
00130     }
00131 
00132     /* Walk the wpos_ci_lclist once to find the total number of
00133      * nodes and the starting locations for each level. */
00134     nodes = lc_idx = ci_idx = rc_idx = 0;
00135     for (i = 0; i < N_WORD_POSN; ++i) {
00136         int j;
00137         for (j = 0; j < mdef->n_ciphone; ++j) {
00138             ph_lc_t *lc;
00139 
00140             for (lc = mdef->wpos_ci_lclist[i][j]; lc; lc = lc->next) {
00141                 ph_rc_t *rc;
00142                 for (rc = lc->rclist; rc; rc = rc->next) {
00143                     ++nodes;    /* RC node */
00144                 }
00145                 ++nodes;        /* LC node */
00146                 ++rc_idx;       /* Start of RC nodes (after LC nodes) */
00147             }
00148             ++nodes;            /* CI node */
00149             ++lc_idx;           /* Start of LC nodes (after CI nodes) */
00150             ++rc_idx;           /* Start of RC nodes (after CI and LC nodes) */
00151         }
00152         ++nodes;                /* wpos node */
00153         ++ci_idx;               /* Start of CI nodes (after wpos nodes) */
00154         ++lc_idx;               /* Start of LC nodes (after CI nodes) */
00155         ++rc_idx;               /* STart of RC nodes (after wpos, CI, and LC nodes) */
00156     }
00157     E_INFO
00158         ("cd_tree: nodes %d wpos start 0 ci start %d lc start %d rc start %d\n",
00159          nodes, ci_idx, lc_idx, rc_idx);
00160     bmdef->n_cd_tree = nodes;
00161     bmdef->cd_tree = ckd_calloc(nodes, sizeof(*bmdef->cd_tree));
00162     for (i = 0; i < N_WORD_POSN; ++i) {
00163         int j;
00164 
00165         bmdef->cd_tree[i].ctx = i;
00166         bmdef->cd_tree[i].n_down = mdef->n_ciphone;
00167         bmdef->cd_tree[i].c.down = ci_idx;
00168 #if 0
00169         E_INFO("%d => %c (%d@%d)\n",
00170                i, (WPOS_NAME)[i],
00171                bmdef->cd_tree[i].n_down, bmdef->cd_tree[i].c.down);
00172 #endif
00173 
00174         /* Now we can build the rest of the tree. */
00175         for (j = 0; j < mdef->n_ciphone; ++j) {
00176             ph_lc_t *lc;
00177 
00178             bmdef->cd_tree[ci_idx].ctx = j;
00179             bmdef->cd_tree[ci_idx].c.down = lc_idx;
00180             for (lc = mdef->wpos_ci_lclist[i][j]; lc; lc = lc->next) {
00181                 ph_rc_t *rc;
00182 
00183                 bmdef->cd_tree[lc_idx].ctx = lc->lc;
00184                 bmdef->cd_tree[lc_idx].c.down = rc_idx;
00185                 for (rc = lc->rclist; rc; rc = rc->next) {
00186                     bmdef->cd_tree[rc_idx].ctx = rc->rc;
00187                     bmdef->cd_tree[rc_idx].n_down = 0;
00188                     bmdef->cd_tree[rc_idx].c.pid = rc->pid;
00189 #if 0
00190                     E_INFO("%d => %s %s %s %c (%d@%d)\n",
00191                            rc_idx,
00192                            bmdef->ciname[j],
00193                            bmdef->ciname[lc->lc],
00194                            bmdef->ciname[rc->rc],
00195                            (WPOS_NAME)[i],
00196                            bmdef->cd_tree[rc_idx].n_down,
00197                            bmdef->cd_tree[rc_idx].c.down);
00198 #endif
00199 
00200                     ++bmdef->cd_tree[lc_idx].n_down;
00201                     ++rc_idx;
00202                 }
00203                 /* If there are no triphones here,
00204                  * this is considered a leafnode, so
00205                  * set the pid to -1. */
00206                 if (bmdef->cd_tree[lc_idx].n_down == 0)
00207                     bmdef->cd_tree[lc_idx].c.pid = -1;
00208 #if 0
00209                 E_INFO("%d => %s %s %c (%d@%d)\n",
00210                        lc_idx,
00211                        bmdef->ciname[j],
00212                        bmdef->ciname[lc->lc],
00213                        (WPOS_NAME)[i],
00214                        bmdef->cd_tree[lc_idx].n_down,
00215                        bmdef->cd_tree[lc_idx].c.down);
00216 #endif
00217 
00218                 ++bmdef->cd_tree[ci_idx].n_down;
00219                 ++lc_idx;
00220             }
00221 
00222             /* As above, so below. */
00223             if (bmdef->cd_tree[ci_idx].n_down == 0)
00224                 bmdef->cd_tree[ci_idx].c.pid = -1;
00225 #if 0
00226             E_INFO("%d => %d=%s (%d@%d)\n",
00227                    ci_idx, j, bmdef->ciname[j],
00228                    bmdef->cd_tree[ci_idx].n_down,
00229                    bmdef->cd_tree[ci_idx].c.down);
00230 #endif
00231 
00232             ++ci_idx;
00233         }
00234     }
00235 
00236     mdef_free(mdef);
00237 
00238     bmdef->alloc_mode = BIN_MDEF_FROM_TEXT;
00239     return bmdef;
00240 }
00241 
00242 void
00243 bin_mdef_free(bin_mdef_t * m)
00244 {
00245     switch (m->alloc_mode) {
00246     case BIN_MDEF_FROM_TEXT:
00247         ckd_free(m->ciname[0]);
00248         ckd_free(m->sseq[0]);
00249         ckd_free(m->phone);
00250         ckd_free(m->cd_tree);
00251         break;
00252     case BIN_MDEF_IN_MEMORY:
00253         ckd_free(m->ciname[0]);
00254         break;
00255     case BIN_MDEF_ON_DISK:
00256         break;
00257     }
00258     if (m->filemap)
00259         mmio_file_unmap(m->filemap);
00260     ckd_free(m->cd2cisen);
00261     ckd_free(m->sen2cimap);
00262     ckd_free(m->ciname);
00263     ckd_free(m->sseq);
00264     ckd_free(m);
00265 }
00266 
00267 static const char format_desc[] =
00268     "BEGIN FILE FORMAT DESCRIPTION\n"
00269     "int32 n_ciphone;    /**< Number of base (CI) phones */\n"
00270     "int32 n_phone;          /**< Number of base (CI) phones + (CD) triphones */\n"
00271     "int32 n_emit_state; /**< Number of emitting states per phone (0 if heterogeneous) */\n"
00272     "int32 n_ci_sen;     /**< Number of CI senones; these are the first */\n"
00273     "int32 n_sen;            /**< Number of senones (CI+CD) */\n"
00274     "int32 n_tmat;           /**< Number of transition matrices */\n"
00275     "int32 n_sseq;       /**< Number of unique senone sequences */\n"
00276     "int32 n_ctx;            /**< Number of phones of context */\n"
00277     "int32 n_cd_tree;    /**< Number of nodes in CD tree structure */\n"
00278     "int32 sil;      /**< CI phone ID for silence */\n"
00279     "char ciphones[][];  /**< CI phone strings (null-terminated) */\n"
00280     "char padding[];     /**< Padding to a 4-bytes boundary */\n"
00281     "struct { int16 ctx; int16 n_down; int32 pid/down } cd_tree[];\n"
00282     "struct { int32 ssid; int32 tmat; int8 attr[4] } phones[];\n"
00283     "int16 sseq[];       /**< Unique senone sequences */\n"
00284     "int8 sseq_len[];    /**< Number of states in each sseq (none if homogeneous) */\n"
00285     "END FILE FORMAT DESCRIPTION\n";
00286 
00287 bin_mdef_t *
00288 bin_mdef_read(cmd_ln_t *config, const char *filename)
00289 {
00290     bin_mdef_t *m;
00291     FILE *fh;
00292     size_t tree_start;
00293     int32 val, i, swap, pos, end;
00294     int32 *sseq_size;
00295     int do_mmap;
00296 
00297     /* Try to read it as text first. */
00298     if ((m = bin_mdef_read_text(config, filename)) != NULL)
00299         return m;
00300 
00301     E_INFO("Reading binary model definition: %s\n", filename);
00302     if ((fh = fopen(filename, "rb")) == NULL)
00303         return NULL;
00304 
00305     if (fread(&val, 4, 1, fh) != 1)
00306         E_FATAL_SYSTEM("Failed to read byte-order marker from %s\n",
00307                        filename);
00308     swap = 0;
00309     if (val == BIN_MDEF_OTHER_ENDIAN) {
00310         swap = 1;
00311         E_INFO("Must byte-swap %s\n", filename);
00312     }
00313     if (fread(&val, 4, 1, fh) != 1)
00314         E_FATAL_SYSTEM("Failed to read version from %s\n", filename);
00315     if (swap)
00316         SWAP_INT32(&val);
00317     if (val > BIN_MDEF_FORMAT_VERSION) {
00318         E_ERROR("File format version %d for %s is newer than library\n",
00319                 val, filename);
00320         fclose(fh);
00321         return NULL;
00322     }
00323     if (fread(&val, 4, 1, fh) != 1)
00324         E_FATAL_SYSTEM("Failed to read header length from %s\n", filename);
00325     if (swap)
00326         SWAP_INT32(&val);
00327     /* Skip format descriptor. */
00328     fseek(fh, val, SEEK_CUR);
00329 
00330     /* Finally allocate it. */
00331     m = ckd_calloc(1, sizeof(*m));
00332 
00333     /* Don't bother to check each one, since they will all fail if one failed. */
00334     fread(&m->n_ciphone, 4, 1, fh);
00335     if (swap)
00336         SWAP_INT32(&m->n_ciphone);
00337     fread(&m->n_phone, 4, 1, fh);
00338     if (swap)
00339         SWAP_INT32(&m->n_phone);
00340     fread(&m->n_emit_state, 4, 1, fh);
00341     if (swap)
00342         SWAP_INT32(&m->n_emit_state);
00343     fread(&m->n_ci_sen, 4, 1, fh);
00344     if (swap)
00345         SWAP_INT32(&m->n_ci_sen);
00346     fread(&m->n_sen, 4, 1, fh);
00347     if (swap)
00348         SWAP_INT32(&m->n_sen);
00349     fread(&m->n_tmat, 4, 1, fh);
00350     if (swap)
00351         SWAP_INT32(&m->n_tmat);
00352     fread(&m->n_sseq, 4, 1, fh);
00353     if (swap)
00354         SWAP_INT32(&m->n_sseq);
00355     fread(&m->n_ctx, 4, 1, fh);
00356     if (swap)
00357         SWAP_INT32(&m->n_ctx);
00358     fread(&m->n_cd_tree, 4, 1, fh);
00359     if (swap)
00360         SWAP_INT32(&m->n_cd_tree);
00361     if (fread(&val, 4, 1, fh) != 1)
00362         E_FATAL_SYSTEM("Failed to read header from %s\n", filename);
00363     m->sil = val;
00364 
00365     /* CI names are first in the file. */
00366     m->ciname = ckd_calloc(m->n_ciphone, sizeof(*m->ciname));
00367 
00368     /* Decide whether to read in the whole file or mmap it. */
00369     do_mmap = config ? cmd_ln_boolean_r(config, "-mmap") : TRUE;
00370     if (swap) {
00371         E_WARN("-mmap specified, but mdef is other-endian.  Will not memory-map.\n");
00372         do_mmap = FALSE;
00373     } 
00374     /* Actually try to mmap it. */
00375     if (do_mmap) {
00376         m->filemap = mmio_file_read(filename);
00377         if (m->filemap == NULL)
00378             do_mmap = FALSE;
00379     }
00380     pos = ftell(fh);
00381     if (do_mmap) {
00382         /* Get the base pointer from the memory map. */
00383         m->ciname[0] = (char *)mmio_file_ptr(m->filemap) + pos;
00384         /* Success! */
00385         m->alloc_mode = BIN_MDEF_ON_DISK;
00386     }
00387     else {
00388         /* Read everything into memory. */
00389         m->alloc_mode = BIN_MDEF_IN_MEMORY;
00390         fseek(fh, 0, SEEK_END);
00391         end = ftell(fh);
00392         fseek(fh, pos, SEEK_SET);
00393         m->ciname[0] = ckd_malloc(end - pos);
00394         if (fread(m->ciname[0], 1, end - pos, fh) != end - pos)
00395             E_FATAL_SYSTEM("Failed to read %d bytes of data from %s\n",
00396                            end - pos, filename);
00397     }
00398 
00399     for (i = 1; i < m->n_ciphone; ++i)
00400         m->ciname[i] = m->ciname[i - 1] + strlen(m->ciname[i - 1]) + 1;
00401 
00402     /* Skip past the padding. */
00403     tree_start =
00404         m->ciname[i - 1] + strlen(m->ciname[i - 1]) + 1 - m->ciname[0];
00405     tree_start = (tree_start + 3) & ~3;
00406     m->cd_tree = (cd_tree_t *) (m->ciname[0] + tree_start);
00407     if (swap) {
00408         for (i = 0; i < m->n_cd_tree; ++i) {
00409             SWAP_INT16(&m->cd_tree[i].ctx);
00410             SWAP_INT16(&m->cd_tree[i].n_down);
00411             SWAP_INT32(&m->cd_tree[i].c.down);
00412         }
00413     }
00414     m->phone = (mdef_entry_t *) (m->cd_tree + m->n_cd_tree);
00415     if (swap) {
00416         for (i = 0; i < m->n_phone; ++i) {
00417             SWAP_INT32(&m->phone[i].ssid);
00418             SWAP_INT32(&m->phone[i].tmat);
00419         }
00420     }
00421     sseq_size = (int32 *) (m->phone + m->n_phone);
00422     if (swap)
00423         SWAP_INT32(sseq_size);
00424     m->sseq = ckd_calloc(m->n_sseq, sizeof(*m->sseq));
00425     m->sseq[0] = (int16 *) (sseq_size + 1);
00426     if (swap) {
00427         for (i = 0; i < *sseq_size; ++i)
00428             SWAP_INT16(m->sseq[0] + i);
00429     }
00430     if (m->n_emit_state) {
00431         for (i = 1; i < m->n_sseq; ++i)
00432             m->sseq[i] = m->sseq[0] + i * m->n_emit_state;
00433     }
00434     else {
00435         m->sseq_len = (uint8 *) (m->sseq[0] + *sseq_size);
00436         for (i = 1; i < m->n_sseq; ++i)
00437             m->sseq[i] = m->sseq[i - 1] + m->sseq_len[i - 1];
00438     }
00439 
00440     /* Now build the CD-to-CI mappings using the senone sequences.
00441      * This is the only really accurate way to do it, though it is
00442      * still inaccurate in the case of heterogeneous topologies or
00443      * cross-state tying. */
00444     m->cd2cisen = (int16 *) ckd_malloc(m->n_sen * sizeof(*m->cd2cisen));
00445     m->sen2cimap = (int16 *) ckd_malloc(m->n_sen * sizeof(*m->sen2cimap));
00446 
00447     /* Default mappings (identity, none) */
00448     for (i = 0; i < m->n_ci_sen; ++i)
00449         m->cd2cisen[i] = i;
00450     for (; i < m->n_sen; ++i)
00451         m->cd2cisen[i] = -1;
00452     for (i = 0; i < m->n_sen; ++i)
00453         m->sen2cimap[i] = -1;
00454     for (i = 0; i < m->n_phone; ++i) {
00455         int32 j, ssid = m->phone[i].ssid;
00456 
00457         for (j = 0; j < bin_mdef_n_emit_state_phone(m, i); ++j) {
00458             int s = bin_mdef_sseq2sen(m, ssid, j);
00459             int ci = bin_mdef_pid2ci(m, i);
00460             /* Take the first one and warn if we have cross-state tying. */
00461             if (m->sen2cimap[s] == -1)
00462                 m->sen2cimap[s] = ci;
00463             if (m->sen2cimap[s] != ci)
00464                 E_WARN
00465                     ("Senone %d is shared between multiple base phones\n",
00466                      s);
00467 
00468             if (j > bin_mdef_n_emit_state_phone(m, ci))
00469                 E_WARN("CD phone %d has fewer states than CI phone %d\n",
00470                        i, ci);
00471             else
00472                 m->cd2cisen[s] =
00473                     bin_mdef_sseq2sen(m, m->phone[ci].ssid, j);
00474         }
00475     }
00476 
00477     /* Set the silence phone. */
00478     m->sil = bin_mdef_ciphone_id(m, S3_SILENCE_CIPHONE);
00479 
00480     E_INFO
00481         ("%d CI-phone, %d CD-phone, %d emitstate/phone, %d CI-sen, %d Sen, %d Sen-Seq\n",
00482          m->n_ciphone, m->n_phone - m->n_ciphone, m->n_emit_state,
00483          m->n_ci_sen, m->n_sen, m->n_sseq);
00484     fclose(fh);
00485     return m;
00486 }
00487 
00488 int
00489 bin_mdef_write(bin_mdef_t * m, const char *filename)
00490 {
00491     FILE *fh;
00492     int32 val, i;
00493 
00494     if ((fh = fopen(filename, "wb")) == NULL)
00495         return -1;
00496 
00497     /* Byteorder marker. */
00498     val = BIN_MDEF_NATIVE_ENDIAN;
00499     fwrite(&val, 1, 4, fh);
00500     /* Version. */
00501     val = BIN_MDEF_FORMAT_VERSION;
00502     fwrite(&val, 1, sizeof(val), fh);
00503 
00504     /* Round the format descriptor size up to a 4-byte boundary. */
00505     val = ((sizeof(format_desc) + 3) & ~3);
00506     fwrite(&val, 1, sizeof(val), fh);
00507     fwrite(format_desc, 1, sizeof(format_desc), fh);
00508     /* Pad it with zeros. */
00509     i = 0;
00510     fwrite(&i, 1, val - sizeof(format_desc), fh);
00511 
00512     /* Binary header things. */
00513     fwrite(&m->n_ciphone, 4, 1, fh);
00514     fwrite(&m->n_phone, 4, 1, fh);
00515     fwrite(&m->n_emit_state, 4, 1, fh);
00516     fwrite(&m->n_ci_sen, 4, 1, fh);
00517     fwrite(&m->n_sen, 4, 1, fh);
00518     fwrite(&m->n_tmat, 4, 1, fh);
00519     fwrite(&m->n_sseq, 4, 1, fh);
00520     fwrite(&m->n_ctx, 4, 1, fh);
00521     fwrite(&m->n_cd_tree, 4, 1, fh);
00522     /* Write this as a 32-bit value to preserve alignment for the
00523      * non-mmap case (we want things aligned both from the
00524      * beginning of the file and the beginning of the phone
00525      * strings). */
00526     val = m->sil;
00527     fwrite(&val, 4, 1, fh);
00528 
00529     /* Phone strings. */
00530     for (i = 0; i < m->n_ciphone; ++i)
00531         fwrite(m->ciname[i], 1, strlen(m->ciname[i]) + 1, fh);
00532     /* Pad with zeros. */
00533     val = (ftell(fh) + 3) & ~3;
00534     i = 0;
00535     fwrite(&i, 1, val - ftell(fh), fh);
00536 
00537     /* Write CD-tree */
00538     fwrite(m->cd_tree, sizeof(*m->cd_tree), m->n_cd_tree, fh);
00539     /* Write phones */
00540     fwrite(m->phone, sizeof(*m->phone), m->n_phone, fh);
00541     if (m->n_emit_state) {
00542         /* Write size of sseq */
00543         val = m->n_sseq * m->n_emit_state;
00544         fwrite(&val, 4, 1, fh);
00545 
00546         /* Write sseq */
00547         fwrite(m->sseq[0], sizeof(**m->sseq),
00548                m->n_sseq * m->n_emit_state, fh);
00549     }
00550     else {
00551         int32 n;
00552 
00553         /* Calcluate size of sseq */
00554         n = 0;
00555         for (i = 0; i < m->n_sseq; ++i)
00556             n += m->sseq_len[i];
00557 
00558         /* Write size of sseq */
00559         fwrite(&n, 4, 1, fh);
00560 
00561         /* Write sseq */
00562         fwrite(m->sseq[0], sizeof(**m->sseq), n, fh);
00563 
00564         /* Write sseq_len */
00565         fwrite(m->sseq_len, 1, m->n_sseq, fh);
00566     }
00567     fclose(fh);
00568 
00569     return 0;
00570 }
00571 
00572 int
00573 bin_mdef_write_text(bin_mdef_t * m, const char *filename)
00574 {
00575     FILE *fh;
00576     int p, i, n_total_state;
00577 
00578     if (strcmp(filename, "-") == 0)
00579         fh = stdout;
00580     else {
00581         if ((fh = fopen(filename, "w")) == NULL)
00582             return -1;
00583     }
00584 
00585     fprintf(fh, "0.3\n");
00586     fprintf(fh, "%d n_base\n", m->n_ciphone);
00587     fprintf(fh, "%d n_tri\n", m->n_phone - m->n_ciphone);
00588     if (m->n_emit_state)
00589         n_total_state = m->n_phone * (m->n_emit_state + 1);
00590     else {
00591         n_total_state = 0;
00592         for (i = 0; i < m->n_phone; ++i)
00593             n_total_state += m->sseq_len[m->phone[i].ssid] + 1;
00594     }
00595     fprintf(fh, "%d n_state_map\n", n_total_state);
00596     fprintf(fh, "%d n_tied_state\n", m->n_sen);
00597     fprintf(fh, "%d n_tied_ci_state\n", m->n_ci_sen);
00598     fprintf(fh, "%d n_tied_tmat\n", m->n_tmat);
00599     fprintf(fh, "#\n# Columns definitions\n");
00600     fprintf(fh, "#%4s %3s %3s %1s %6s %4s %s\n",
00601             "base", "lft", "rt", "p", "attrib", "tmat",
00602             "     ... state id's ...");
00603 
00604     for (p = 0; p < m->n_ciphone; p++) {
00605         int n_state;
00606 
00607         fprintf(fh, "%5s %3s %3s %1s", m->ciname[p], "-", "-", "-");
00608 
00609         if (bin_mdef_is_fillerphone(m, p))
00610             fprintf(fh, " %6s", "filler");
00611         else
00612             fprintf(fh, " %6s", "n/a");
00613         fprintf(fh, " %4d", m->phone[p].tmat);
00614 
00615         if (m->n_emit_state)
00616             n_state = m->n_emit_state;
00617         else
00618             n_state = m->sseq_len[m->phone[p].ssid];
00619         for (i = 0; i < n_state; i++) {
00620             fprintf(fh, " %6u", m->sseq[m->phone[p].ssid][i]);
00621         }
00622         fprintf(fh, " N\n");
00623     }
00624 
00625 
00626     for (; p < m->n_phone; p++) {
00627         int n_state;
00628 
00629         fprintf(fh, "%5s %3s %3s %c",
00630                 m->ciname[m->phone[p].info.cd.ctx[0]],
00631                 m->ciname[m->phone[p].info.cd.ctx[1]],
00632                 m->ciname[m->phone[p].info.cd.ctx[2]],
00633                 (WPOS_NAME)[m->phone[p].info.cd.wpos]);
00634 
00635         if (bin_mdef_is_fillerphone(m, p))
00636             fprintf(fh, " %6s", "filler");
00637         else
00638             fprintf(fh, " %6s", "n/a");
00639         fprintf(fh, " %4d", m->phone[p].tmat);
00640 
00641 
00642         if (m->n_emit_state)
00643             n_state = m->n_emit_state;
00644         else
00645             n_state = m->sseq_len[m->phone[p].ssid];
00646         for (i = 0; i < n_state; i++) {
00647             fprintf(fh, " %6u", m->sseq[m->phone[p].ssid][i]);
00648         }
00649         fprintf(fh, " N\n");
00650     }
00651 
00652     if (strcmp(filename, "-") != 0)
00653         fclose(fh);
00654     return 0;
00655 }
00656 
00657 int
00658 bin_mdef_ciphone_id(bin_mdef_t * m, const char *ciphone)
00659 {
00660     int low, mid, high;
00661 
00662     /* Exact binary search on m->ciphone */
00663     low = 0;
00664     high = m->n_ciphone;
00665     while (low < high) {
00666         int c;
00667 
00668         mid = (low + high) / 2;
00669         c = strcmp(ciphone, m->ciname[mid]);
00670         if (c == 0)
00671             return mid;
00672         else if (c > 0)
00673             low = mid + 1;
00674         else if (c < 0)
00675             high = mid;
00676     }
00677     return -1;
00678 }
00679 
00680 int
00681 bin_mdef_ciphone_id_nocase(bin_mdef_t * m, const char *ciphone)
00682 {
00683     int low, mid, high;
00684 
00685     /* Exact binary search on m->ciphone */
00686     low = 0;
00687     high = m->n_ciphone;
00688     while (low < high) {
00689         int c;
00690 
00691         mid = (low + high) / 2;
00692         c = strcmp_nocase(ciphone, m->ciname[mid]);
00693         if (c == 0)
00694             return mid;
00695         else if (c > 0)
00696             low = mid + 1;
00697         else if (c < 0)
00698             high = mid;
00699     }
00700     return -1;
00701 }
00702 
00703 const char *
00704 bin_mdef_ciphone_str(bin_mdef_t * m, int32 ci)
00705 {
00706     assert(m != NULL);
00707     assert(ci < m->n_ciphone);
00708     return m->ciname[ci];
00709 }
00710 
00711 int
00712 bin_mdef_phone_id(bin_mdef_t * m, int32 ci, int32 lc, int32 rc, int32 wpos)
00713 {
00714     cd_tree_t *cd_tree;
00715     int level, max;
00716     int16 ctx[4];
00717 
00718     assert(m);
00719 
00720     /* In the future, we might back off when context is not available,
00721      * but for now we'll just return the CI phone. */
00722     if (lc < 0 || rc < 0)
00723         return ci;
00724 
00725     assert((ci >= 0) && (ci < m->n_ciphone));
00726     assert((lc >= 0) && (lc < m->n_ciphone));
00727     assert((rc >= 0) && (rc < m->n_ciphone));
00728     assert((wpos >= 0) && (wpos < N_WORD_POSN));
00729 
00730     /* Create a context list, mapping fillers to silence. */
00731     ctx[0] = wpos;
00732     ctx[1] = ci;
00733     ctx[2] = (m->sil >= 0
00734               && m->phone[lc].info.ci.filler) ? m->sil : lc;
00735     ctx[3] = (m->sil >= 0
00736               && m->phone[rc].info.ci.filler) ? m->sil : rc;
00737 
00738     /* Walk down the cd_tree. */
00739     cd_tree = m->cd_tree;
00740     level = 0;                  /* What level we are on. */
00741     max = N_WORD_POSN;          /* Number of nodes on this level. */
00742     while (level < 4) {
00743         int i;
00744 
00745 #if 0
00746         E_INFO("Looking for context %d=%s in %d at %d\n",
00747                ctx[level], m->ciname[ctx[level]],
00748                max, cd_tree - m->cd_tree);
00749 #endif
00750         for (i = 0; i < max; ++i) {
00751 #if 0
00752             E_INFO("Look at context %d=%s at %d\n",
00753                    cd_tree[i].ctx,
00754                    m->ciname[cd_tree[i].ctx], cd_tree + i - m->cd_tree);
00755 #endif
00756             if (cd_tree[i].ctx == ctx[level])
00757                 break;
00758         }
00759         if (i == max)
00760             return -1;
00761 #if 0
00762         E_INFO("Found context %d=%s at %d, n_down=%d, down=%d\n",
00763                ctx[level], m->ciname[ctx[level]],
00764                cd_tree + i - m->cd_tree,
00765                cd_tree[i].n_down, cd_tree[i].c.down);
00766 #endif
00767         /* Leaf node, stop here. */
00768         if (cd_tree[i].n_down == 0)
00769             return cd_tree[i].c.pid;
00770 
00771         /* Go down one level. */
00772         max = cd_tree[i].n_down;
00773         cd_tree = m->cd_tree + cd_tree[i].c.down;
00774         ++level;
00775     }
00776     /* We probably shouldn't get here. */
00777     return -1;
00778 }
00779 
00780 int
00781 bin_mdef_phone_str(bin_mdef_t * m, int pid, char *buf)
00782 {
00783     char *wpos_name;
00784 
00785     assert(m);
00786     assert((pid >= 0) && (pid < m->n_phone));
00787     wpos_name = WPOS_NAME;
00788 
00789     buf[0] = '\0';
00790     if (pid < m->n_ciphone)
00791         sprintf(buf, "%s", bin_mdef_ciphone_str(m, pid));
00792     else {
00793         sprintf(buf, "%s %s %s %c",
00794                 bin_mdef_ciphone_str(m, m->phone[pid].info.cd.ctx[0]),
00795                 bin_mdef_ciphone_str(m, m->phone[pid].info.cd.ctx[1]),
00796                 bin_mdef_ciphone_str(m, m->phone[pid].info.cd.ctx[2]),
00797                 wpos_name[m->phone[pid].info.cd.wpos]);
00798     }
00799     return 0;
00800 }

Generated on Thu Jan 27 2011 for PocketSphinx by  doxygen 1.7.1