~ubuntu-branches/ubuntu/saucy/sphinxtrain/saucy

« back to all changes in this revision

Viewing changes to src/programs/mk_mdef_gen/mk_mdef_gen.c

  • Committer: Package Import Robot
  • Author(s): Samuel Thibault
  • Date: 2013-01-02 04:10:21 UTC
  • Revision ID: package-import@ubuntu.com-20130102041021-ynsizmz33fx02hea
Tags: upstream-1.0.8
ImportĀ upstreamĀ versionĀ 1.0.8

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* ====================================================================
 
2
 * Copyright (c) 2000 Carnegie Mellon University.  All rights 
 
3
 * reserved.
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions
 
7
 * are met:
 
8
 *
 
9
 * 1. Redistributions of source code must retain the above copyright
 
10
 *    notice, this list of conditions and the following disclaimer. 
 
11
 *
 
12
 * 2. Redistributions in binary form must reproduce the above copyright
 
13
 *    notice, this list of conditions and the following disclaimer in
 
14
 *    the documentation and/or other materials provided with the
 
15
 *    distribution.
 
16
 *
 
17
 * This work was supported in part by funding from the Defense Advanced 
 
18
 * Research Projects Agency and the National Science Foundation of the 
 
19
 * United States of America, and the CMU Sphinx Speech Consortium.
 
20
 *
 
21
 * THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND 
 
22
 * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
 
23
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 
24
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY
 
25
 * NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
26
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
 
27
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
 
28
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
 
29
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 
30
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 
31
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
32
 *
 
33
 * ====================================================================
 
34
 *
 
35
 */
 
36
/*********************************************************************
 
37
 * Various functions used in mk_mdef_gen
 
38
 *
 
39
 * Author: 
 
40
 *    Rita Singh (rsingh@cs.cmu.edu) 
 
41
 *********************************************************************/
 
42
 
 
43
 
 
44
#include <s3/s3.h>
 
45
#include <s3/acmod_set.h>
 
46
#include <s3/model_def_io.h>
 
47
#include <s3/s3.h>
 
48
 
 
49
#include <sphinxbase/ckd_alloc.h>
 
50
#include <sphinxbase/pio.h>
 
51
#include <sphinxbase/cmd_ln.h>
 
52
#include <sphinxbase/err.h>
 
53
 
 
54
#include <stdio.h>
 
55
#include <stdlib.h>
 
56
#include <string.h>
 
57
#include <assert.h>
 
58
#include <time.h>
 
59
 
 
60
#include "parse_cmd_ln.h"
 
61
#include "heap.h"
 
62
#include "hash.h"
 
63
 
 
64
#define CEILING   10000  
 
65
#define IS_FILLER(X)    ((X[0]=='+'||strcmp(X,"SIL")==0) ? 1 : 0)
 
66
 
 
67
 
 
68
static const char* wordpos2str(word_posn_t wordpos, int ignore_wordpos);
 
69
 
 
70
int32 make_ci_list_frm_mdef(const char  *mdeffile,
 
71
                            char  ***CIlist, 
 
72
                            int32 *cilistsize)
 
73
{
 
74
    char  **cilist;
 
75
 
 
76
    int32 nciphns, id, maxphnsize, phnsize;
 
77
    model_def_t *mdef;
 
78
 
 
79
    if (model_def_read(&mdef, mdeffile) != S3_SUCCESS) 
 
80
        E_ERROR("Unable to read mdef file %s\n",mdeffile);
 
81
    *cilistsize = nciphns = mdef->acmod_set->n_ci;
 
82
 
 
83
   /* Find phone length */
 
84
    maxphnsize = 0;
 
85
    for (id = 0; id < nciphns; id++){
 
86
        phnsize = strlen(acmod_set_id2name(mdef->acmod_set,id));
 
87
        if (phnsize > maxphnsize) maxphnsize = phnsize;
 
88
    }
 
89
    *CIlist = cilist = (char**)ckd_calloc_2d(nciphns,maxphnsize+1,sizeof(char));
 
90
    for (id = 0; id < nciphns; id++){
 
91
        strcpy(cilist[id],acmod_set_id2name(mdef->acmod_set,id));
 
92
    }
 
93
 
 
94
    return S3_SUCCESS;
 
95
}
 
96
 
 
97
 
 
98
int32 make_ci_list_cd_hash_frm_phnlist(const char  *phnlist,
 
99
                                    char  ***CIlist, 
 
100
                                    int32 *cilistsize,
 
101
                                    hashelement_t ***CDhash,
 
102
                                    int32 *NCDphones)
 
103
{
 
104
    char  bphn[1024],lctx[1024], rctx[1024], wdpos[1024]; 
 
105
    char  **cilist, *silence="SIL";
 
106
    lineiter_t *line = NULL;
 
107
    heapelement_t **heap=NULL, *addciphone;
 
108
    hashelement_t **tphnhash, *tphnptr;
 
109
    phnhashelement_t  **phnhash, *phnptr;
 
110
 
 
111
    int32 swdtphs, bwdtphs, ewdtphs, iwdtphs, maxphnsize, phnsize;
 
112
    int32 heapsize, i, nciphns, nwds;
 
113
    FILE  *fp;
 
114
 
 
115
    fp = fopen(phnlist,"r");
 
116
    if (fp==NULL) E_FATAL("Unable to open %s for reading\n",phnlist);
 
117
 
 
118
   /* Initially hash everything to remove duplications */
 
119
    phnhash = (phnhashelement_t**)calloc(PHNHASHSIZE,sizeof(phnhashelement_t*));
 
120
    tphnhash = (hashelement_t**) ckd_calloc(HASHSIZE, sizeof(hashelement_t*));
 
121
    maxphnsize = 0;
 
122
    swdtphs = bwdtphs = ewdtphs = iwdtphs = 0;
 
123
    /* Always install SIL in phonelist */
 
124
    phninstall(silence,phnhash);
 
125
 
 
126
    for (line = lineiter_start_clean(fp); line; line = lineiter_next(line)) {
 
127
 
 
128
        nwds = sscanf(line->buf,"%s %s %s %s",bphn,lctx,rctx,wdpos);
 
129
 
 
130
        if (nwds != 1 && nwds != 3 &&  nwds != 4)
 
131
            E_FATAL("Incorrect format in triphone file %s\n%s\n",phnlist,line->buf);
 
132
        if (strcmp(bphn,"-") == 0)
 
133
            E_FATAL("Bad entry triphone file %s\n%s\n",phnlist,line->buf);
 
134
            
 
135
        if (nwds == 1 || (!strcmp(lctx,"-") && !strcmp(rctx,"-"))) {
 
136
            phnsize = strlen(bphn);
 
137
            if (phnsize > maxphnsize) maxphnsize = phnsize;
 
138
            phninstall(bphn,phnhash);
 
139
        }
 
140
        else {
 
141
            if (nwds == 3) strcpy(wdpos,"i"); /* default */
 
142
            if (!strcmp(lctx,"-") && !strcmp(rctx,"-") && !strcmp(wdpos,"-")){
 
143
                E_WARN("Bad entry triphone file %s\n%s\n",phnlist,line->buf);
 
144
                continue;
 
145
            }
 
146
            if (IS_FILLER(lctx) || IS_FILLER(rctx)){
 
147
                E_WARN("Fillers in triphone, skipping:\n%s\n",line->buf);
 
148
                continue;
 
149
            }
 
150
            if (!strcmp(wdpos,"i") && !strcmp(wdpos,"b") && 
 
151
                !strcmp(wdpos,"e") && !strcmp(wdpos,"s")){
 
152
                E_WARN("Bad word position type %s in triphone list\n",wdpos);
 
153
                E_WARN("Mapping it to word internal triphone\n");
 
154
                strcpy(wdpos,"i");
 
155
            }
 
156
            tphnptr = install(bphn,lctx,rctx,wdpos,tphnhash);
 
157
            if (tphnptr->dictcount == 0){
 
158
                if (strcmp(wdpos,"s")==0) swdtphs++;
 
159
                else if (strcmp(wdpos,"b")==0) bwdtphs++;
 
160
                else if (strcmp(wdpos,"e")==0) ewdtphs++;
 
161
                else if (strcmp(wdpos,"i")==0) iwdtphs++;
 
162
                tphnptr->dictcount++;
 
163
            }
 
164
        }
 
165
    }
 
166
    fclose(fp);
 
167
 
 
168
    /* Heap sort CI phones */
 
169
    heapsize = 0;
 
170
    for (i = 0; i < PHNHASHSIZE; i++) {
 
171
        phnptr = phnhash[i];
 
172
        while (phnptr != NULL) {
 
173
            addciphone = (heapelement_t *) ckd_calloc(1,sizeof(heapelement_t));
 
174
            addciphone->basephone = strdup(phnptr->phone);
 
175
            heapsize = insert(&heap, heapsize, addciphone);
 
176
            phnptr = phnptr->next;
 
177
        }
 
178
    }
 
179
    freephnhash(phnhash);
 
180
    *cilistsize = nciphns = heapsize;
 
181
    *CIlist = cilist = (char**)ckd_calloc_2d(nciphns,maxphnsize+1,sizeof(char));
 
182
    for (i = 0; i < nciphns; i++){
 
183
        addciphone = yanktop(&heap,heapsize,&heapsize);
 
184
        strcpy(cilist[i],addciphone->basephone);
 
185
        free_heapelement(addciphone);
 
186
    }
 
187
    assert(heapsize == 0);
 
188
 
 
189
    E_INFO("%d single word triphones in input phone list\n",swdtphs);
 
190
    E_INFO("%d word beginning triphones in input phone list\n",bwdtphs);
 
191
    E_INFO("%d word internal triphones in input phone list\n",iwdtphs);
 
192
    E_INFO("%d word ending triphones in input phone list\n",ewdtphs);
 
193
 
 
194
    *CDhash = tphnhash;
 
195
    *NCDphones = swdtphs + bwdtphs + iwdtphs + ewdtphs;
 
196
 
 
197
    return S3_SUCCESS;
 
198
}
 
199
 
 
200
 
 
201
int32 make_ci_list_cd_hash_frm_mdef(const char  *mdeffile,
 
202
                                    char  ***CIlist, 
 
203
                                    int32 *cilistsize,
 
204
                                    hashelement_t ***CDhash,
 
205
                                    int32 *NCDphones)
 
206
{
 
207
    char  bphn[1024],lctx[1024], rctx[1024], wdpos[1024]; 
 
208
    char  **cilist;
 
209
    hashelement_t **tphnhash, *tphnptr;
 
210
 
 
211
    int32 swdtphs, bwdtphs, ewdtphs, iwdtphs, maxphnsize, phnsize;
 
212
    int32 nciphns, id, n_acmod;
 
213
    model_def_t *mdef;
 
214
 
 
215
    if (model_def_read(&mdef, mdeffile) != S3_SUCCESS) 
 
216
        E_ERROR("Unable to read mdef file %s\n",mdeffile);
 
217
    *cilistsize = nciphns = mdef->acmod_set->n_ci;
 
218
    n_acmod = acmod_set_n_acmod(mdef->acmod_set);
 
219
 
 
220
   /* Find Max phone length */
 
221
    maxphnsize = 0;
 
222
    for (id = 0; id < nciphns; id++){
 
223
        phnsize = strlen(acmod_set_id2name(mdef->acmod_set,id));
 
224
        if (phnsize > maxphnsize) maxphnsize = phnsize;
 
225
    }
 
226
    *CIlist = cilist = (char**)ckd_calloc_2d(nciphns,maxphnsize+1,sizeof(char));
 
227
    for (id = 0; id < nciphns; id++){
 
228
        strcpy(cilist[id],acmod_set_id2name(mdef->acmod_set,id));
 
229
    }
 
230
 
 
231
    tphnhash = (hashelement_t**) ckd_calloc(HASHSIZE, sizeof(hashelement_t*));
 
232
    swdtphs = bwdtphs = ewdtphs = iwdtphs = 0;
 
233
    for (;id < n_acmod; id++){
 
234
        sscanf(acmod_set_id2name(mdef->acmod_set, id),"%s %s %s %s",
 
235
                bphn,lctx,rctx,wdpos);
 
236
        tphnptr = install(bphn,lctx,rctx,wdpos,tphnhash);
 
237
        if (tphnptr->dictcount == 0){
 
238
            if (strcmp(wdpos,"s")==0) swdtphs++;
 
239
            else if (strcmp(wdpos,"b")==0) bwdtphs++;
 
240
            else if (strcmp(wdpos,"e")==0) ewdtphs++;
 
241
            else if (strcmp(wdpos,"i")==0) iwdtphs++;
 
242
            tphnptr->dictcount++;
 
243
        }
 
244
    }
 
245
 
 
246
    E_INFO("%d single word triphones in triphone list\n",swdtphs);
 
247
    E_INFO("%d word beginning triphones in triphone list\n",bwdtphs);
 
248
    E_INFO("%d word internal triphones in triphone list\n",iwdtphs);
 
249
    E_INFO("%d word ending triphones in triphone list\n",ewdtphs);
 
250
 
 
251
    *CDhash = tphnhash;
 
252
    *NCDphones = swdtphs + bwdtphs + iwdtphs + ewdtphs;
 
253
 
 
254
    return S3_SUCCESS;
 
255
}
 
256
 
 
257
 
 
258
int32  read_dict(const char *dictfile, const char *fillerdict, 
 
259
                 dicthashelement_t ***dicthash)
 
260
{
 
261
    char *dictsent;
 
262
    const char *dictfn[2];
 
263
    lineiter_t *liter = NULL;
 
264
    char  *dictword, *word, *phone, *tphn;
 
265
    dicthashelement_t   **lhash, *sptr;
 
266
    int32  maxphnlen, vocabsiz=0, nphns, numdicts, idict;
 
267
    FILE   *dict;
 
268
 
 
269
    numdicts = 1;  dictfn[0] = dictfile;
 
270
    if (fillerdict != NULL){
 
271
        dictfn[1] = fillerdict; 
 
272
        numdicts = 2;
 
273
    }
 
274
 
 
275
    lhash = (dicthashelement_t**)calloc(DICTHASHSIZE,sizeof(dicthashelement_t));
 
276
    if (lhash == NULL) E_FATAL("Unable to allocate space for dictionary\n");
 
277
 
 
278
    /* Create hash table with dictionary words as entries */
 
279
    for (idict = 0; idict < numdicts; idict++){
 
280
        E_INFO("Reading dict %s\n",dictfn[idict]);
 
281
        if ((dict = fopen(dictfn[idict],"r")) == NULL)
 
282
            E_FATAL_SYSTEM("Unable to open dictionary %s", dictfile);
 
283
 
 
284
        vocabsiz = 0;
 
285
        for (liter = lineiter_start_clean(dict); liter; liter = lineiter_next(liter)) {
 
286
 
 
287
            if (liter->buf[0] == 0) {
 
288
                E_WARN("Empty line %d in the dictionary file %s\n", 
 
289
                        lineiter_lineno(liter), dictfn[idict]);
 
290
                continue;
 
291
            }
 
292
 
 
293
            dictsent = strdup(liter->buf);
 
294
            if ((dictword = strtok(dictsent, " \t\n")) == NULL)
 
295
                E_FATAL("Empty line in dictionary!\n");
 
296
 
 
297
            if ((sptr = dictinstall(dictword,lhash)) == NULL)
 
298
                E_FATAL("Unable to install dict word %s\n",dictword);
 
299
 
 
300
            if (sptr->nphns != 0)
 
301
                E_FATAL("Duplicate entry for %s in dictionary\n",dictword);
 
302
 
 
303
            /* Count number of phones in pronunciation */
 
304
            maxphnlen = 0;
 
305
            for(nphns=0; (tphn=strtok(NULL," \t\n")) != NULL; nphns++){
 
306
                if (strlen(tphn) > maxphnlen) maxphnlen = strlen(tphn);
 
307
            }
 
308
            if (nphns == 0)
 
309
                E_FATAL("Dictionary word %s has no pronunciation\n",dictword);
 
310
            sptr->nphns = nphns; maxphnlen++;
 
311
            sptr->phones = (char**)ckd_calloc_2d(nphns,maxphnlen,sizeof(char));
 
312
 
 
313
            word = strtok(liter->buf," \t\n");
 
314
            for(nphns=0;(phone = strtok(NULL," \t\n")) != NULL;nphns++)
 
315
                strcpy(sptr->phones[nphns],phone);
 
316
        
 
317
            ++vocabsiz;
 
318
            
 
319
            free(dictsent);
 
320
        }
 
321
        fclose(dict);
 
322
        E_INFO("%d words in dict %s\n", vocabsiz, dictfn[idict]);
 
323
    }
 
324
 
 
325
    *dicthash = lhash;
 
326
 
 
327
    lineiter_free(liter);
 
328
    return(vocabsiz);
 
329
}
 
330
 
 
331
    
 
332
int32 make_dict_triphone_list (dicthashelement_t **dicthash,
 
333
                          hashelement_t ***triphonehash,
 
334
                          int ignore_wpos)
 
335
{
 
336
    hashelement_t **tphnhash, *tphnptr;
 
337
    dicthashelement_t *word_el;
 
338
    phnhashelement_t *bphnptr, **bphnhash, *ephnptr, **ephnhash;
 
339
    char *bphn, *lctx, *rctx;
 
340
    const char* wpos;
 
341
    char *silencephn = "SIL";
 
342
    int32 totaltphs, totwds, bwdtphs, ewdtphs, iwdtphs, swdtphs, lnphns;
 
343
    int32 i,j,k;
 
344
 
 
345
    tphnhash = (hashelement_t**) ckd_calloc(HASHSIZE, sizeof(hashelement_t));
 
346
    bphnhash = (phnhashelement_t**)calloc(PHNHASHSIZE,sizeof(phnhashelement_t));
 
347
    ephnhash = (phnhashelement_t**)calloc(PHNHASHSIZE,sizeof(phnhashelement_t));
 
348
    if (bphnhash == NULL || ephnhash == NULL)
 
349
        E_FATAL("Unable to alloc %d size phone hashtables!\n",(int)PHNHASHSIZE);
 
350
 
 
351
    /*First count all phones that can begin or end a word (SIL can by default)*/
 
352
    phninstall(silencephn,bphnhash);
 
353
    phninstall(silencephn,ephnhash);
 
354
    for (i = 0; i < DICTHASHSIZE; i++){
 
355
        word_el = dicthash[i];
 
356
        while (word_el != NULL){
 
357
            if (!IS_FILLER(word_el->phones[0]))
 
358
                phninstall(word_el->phones[0],bphnhash);
 
359
            if (!IS_FILLER(word_el->phones[word_el->nphns - 1]))
 
360
                phninstall(word_el->phones[word_el->nphns - 1],ephnhash);
 
361
            word_el = word_el->next;
 
362
        }
 
363
    }
 
364
 
 
365
    /* Scan dictionary and make triphone list */
 
366
    totwds = bwdtphs = ewdtphs = iwdtphs = swdtphs = 0;
 
367
    for (i = 0; i < DICTHASHSIZE; i++){
 
368
        word_el = dicthash[i];
 
369
        while (word_el != NULL){
 
370
            totwds++;
 
371
            lnphns = word_el->nphns;
 
372
            if (lnphns == 1) {
 
373
                wpos = wordpos2str(WORD_POSN_SINGLE, ignore_wpos);
 
374
                bphn = word_el->phones[0];
 
375
                if (IS_FILLER(bphn)) {word_el = word_el->next; continue;}
 
376
                for (j = 0; j < PHNHASHSIZE; j++){
 
377
                    ephnptr = ephnhash[j];
 
378
                    while (ephnptr != NULL){
 
379
                        lctx = ephnptr->phone;
 
380
                        for (k = 0; k < PHNHASHSIZE; k++){
 
381
                            bphnptr = bphnhash[k];
 
382
                            while (bphnptr != NULL){
 
383
                                rctx = bphnptr->phone;
 
384
                                tphnptr = install(bphn,lctx,rctx,wpos,tphnhash);
 
385
                                if (tphnptr->dictcount == 0) swdtphs++;
 
386
                                tphnptr->dictcount++;
 
387
                                bphnptr = bphnptr->next;
 
388
                            }
 
389
                        }
 
390
                        ephnptr = ephnptr->next;
 
391
                    }
 
392
                }
 
393
            }
 
394
            else {
 
395
                wpos = wordpos2str(WORD_POSN_BEGIN, ignore_wpos);
 
396
                bphn = word_el->phones[0];
 
397
                if (IS_FILLER(bphn)) {word_el = word_el->next; continue;}
 
398
                rctx = word_el->phones[1];
 
399
                if (IS_FILLER(rctx)) rctx = silencephn;
 
400
                for (j = 0; j < PHNHASHSIZE; j++){
 
401
                    ephnptr = ephnhash[j];
 
402
                    while (ephnptr != NULL){
 
403
                        lctx = ephnptr->phone;
 
404
                        tphnptr = install(bphn,lctx,rctx,wpos,tphnhash);
 
405
                        if (tphnptr->dictcount == 0) bwdtphs++;
 
406
                        tphnptr->dictcount++;
 
407
                        ephnptr = ephnptr->next;
 
408
                    }
 
409
                }
 
410
                wpos = wordpos2str(WORD_POSN_INTERNAL, ignore_wpos);
 
411
                for (j=1;j<lnphns-1;j++){
 
412
                    bphn = word_el->phones[j];
 
413
                    if (IS_FILLER(bphn)) continue;
 
414
                    lctx = word_el->phones[j-1];
 
415
                    if (IS_FILLER(lctx)) lctx = silencephn;
 
416
                    rctx = word_el->phones[j+1];
 
417
                    if (IS_FILLER(rctx)) rctx = silencephn;
 
418
                    tphnptr = install(bphn,lctx,rctx,wpos,tphnhash);
 
419
                    if (tphnptr->dictcount == 0) iwdtphs++;
 
420
                    tphnptr->dictcount++;
 
421
                }
 
422
                wpos = wordpos2str(WORD_POSN_END, ignore_wpos);
 
423
                bphn = word_el->phones[lnphns-1];
 
424
                if (IS_FILLER(bphn)) {word_el = word_el->next; continue;}
 
425
                lctx = word_el->phones[lnphns-2];
 
426
                if (IS_FILLER(lctx)) lctx = silencephn;
 
427
                for (j = 0; j < PHNHASHSIZE; j++){
 
428
                    bphnptr = bphnhash[j];
 
429
                    while (bphnptr != NULL){
 
430
                        rctx = bphnptr->phone;
 
431
                        tphnptr = install(bphn,lctx,rctx,wpos,tphnhash);
 
432
                        if (tphnptr->dictcount == 0) ewdtphs++;
 
433
                        tphnptr->dictcount++;
 
434
                        bphnptr = bphnptr->next;
 
435
                    }
 
436
                }
 
437
            }
 
438
            word_el = word_el->next;
 
439
        }
 
440
    }
 
441
    totaltphs = swdtphs + bwdtphs + iwdtphs + ewdtphs;
 
442
    E_INFO("%d words in dictionary\n",totwds);
 
443
    E_INFO("%d unique single word triphones in dictionary\n",swdtphs);
 
444
    E_INFO("%d unique word beginning triphones in dictionary\n",bwdtphs);
 
445
    E_INFO("%d unique word internal triphones in dictionary\n",iwdtphs);
 
446
    E_INFO("%d unique word ending triphones in dictionary\n",ewdtphs);
 
447
 
 
448
    *triphonehash = tphnhash;
 
449
 
 
450
    freephnhash(bphnhash); freephnhash(ephnhash);
 
451
 
 
452
    return S3_SUCCESS;
 
453
}
 
454
 
 
455
 
 
456
int32  count_triphones (const char *transfile,
 
457
                        dicthashelement_t **dicthash,
 
458
                        hashelement_t **tphnhash,
 
459
                        phnhashelement_t ***phnhash,
 
460
                        int ignore_wpos)
 
461
{
 
462
    int32  nbwdtphns, newdtphns, niwdtphns, nswdtphns;
 
463
    int32  nwords, lnphns, n_totalwds;
 
464
    lineiter_t *line = NULL;
 
465
    char *tline;
 
466
    char   *word, *basephone, *lctxt, *rctxt; 
 
467
    char   silencephn[4];
 
468
    const char* wpos;
 
469
    int32  i, j;
 
470
    dicthashelement_t **wordarr;
 
471
    phnhashelement_t **lphnhash, *phnptr;
 
472
    hashelement_t *tphnptr;
 
473
    FILE   *fp;
 
474
 
 
475
    strcpy(silencephn,"SIL");
 
476
    if ((fp = fopen(transfile,"r")) == NULL)
 
477
        E_FATAL("Unable to open transcript file %s for reading!\n",transfile);
 
478
 
 
479
    E_INFO("Out of vocabulary words in transcript will be mapped to SIL!\n");
 
480
 
 
481
    lphnhash = (phnhashelement_t**)calloc(PHNHASHSIZE,sizeof(phnhashelement_t));
 
482
 
 
483
    n_totalwds = 0;
 
484
    nbwdtphns = newdtphns = niwdtphns = nswdtphns = 0;
 
485
 
 
486
    for (line = lineiter_start_clean(fp); line; line = lineiter_next(line)) {
 
487
 
 
488
        tline = strdup(line->buf);
 
489
        if (strtok(tline," \t\n") == NULL) {
 
490
           free(tline);
 
491
           continue;
 
492
        }
 
493
 
 
494
        /* Count number of phones in pronunciation */
 
495
        for(nwords=1; strtok(NULL," \t\n") != NULL; nwords++);
 
496
        n_totalwds += nwords;
 
497
        wordarr = (dicthashelement_t **)ckd_calloc(nwords+2,sizeof(dicthashelement_t*));
 
498
        word = strtok(line->buf," \t\n"); 
 
499
        if ((wordarr[1] = dictlookup(word,dicthash)) == NULL) {
 
500
            E_WARN("Word %s not found in dictionary. Mapping to SIL.\n", word);
 
501
        }
 
502
        for (j=2; (word = strtok(NULL," \t\n")) != NULL; j++) {
 
503
            if ((wordarr[j] = dictlookup(word,dicthash)) == NULL) {
 
504
                /* If word is surrounded by "()", assume it's the
 
505
                 * utterance ID, and don't report it as an OOV */
 
506
                if ((word[0] != '(') && (word[strlen(word) - 1] != ')')) {
 
507
                    E_WARN("Word %s not found in dictionary. Mapping to SIL.\n", word);
 
508
                }
 
509
            }
 
510
        }       
 
511
        for (i=1; i<=nwords; i++){/* Indices account for padded wordarr array */
 
512
            if (wordarr[i] == NULL) continue;
 
513
 
 
514
            lnphns = wordarr[i]->nphns;
 
515
            if (lnphns == 1) {
 
516
                wpos = wordpos2str(WORD_POSN_SINGLE, ignore_wpos);
 
517
                basephone = wordarr[i]->phones[0];
 
518
                phnptr = phninstall(basephone,lphnhash);
 
519
                phnptr->count++;
 
520
                if (IS_FILLER(basephone)) continue;
 
521
                if (wordarr[i-1] != NULL){
 
522
                    lctxt = wordarr[i-1]->phones[wordarr[i-1]->nphns - 1];
 
523
                    if (IS_FILLER(lctxt)) lctxt = silencephn;
 
524
                }
 
525
                else lctxt = silencephn;
 
526
                if (wordarr[i+1] != NULL){
 
527
                    rctxt = wordarr[i+1]->phones[0];
 
528
                    if (IS_FILLER(rctxt)) rctxt = silencephn;
 
529
                }
 
530
                else rctxt = silencephn;
 
531
                tphnptr = lookup(basephone,lctxt,rctxt,wpos,tphnhash);
 
532
                (tphnptr->count)++;
 
533
                nswdtphns++;
 
534
            }
 
535
            else {
 
536
                wpos = wordpos2str(WORD_POSN_BEGIN, ignore_wpos);
 
537
                basephone = wordarr[i]->phones[0];
 
538
                phnptr = phninstall(basephone,lphnhash);
 
539
                phnptr->count++;
 
540
                if (IS_FILLER(basephone)) continue;
 
541
                if (!IS_FILLER(basephone)){
 
542
                    if (wordarr[i-1] != NULL){
 
543
                        lctxt = wordarr[i-1]->phones[wordarr[i-1]->nphns - 1];
 
544
                        if (IS_FILLER(lctxt)) lctxt = silencephn;
 
545
                    }
 
546
                    else lctxt = silencephn;
 
547
                    rctxt = wordarr[i]->phones[1];
 
548
                    if (IS_FILLER(rctxt)) rctxt = silencephn;
 
549
                    tphnptr = lookup(basephone,lctxt,rctxt,wpos,tphnhash);
 
550
                    (tphnptr->count)++; 
 
551
                    nbwdtphns++;
 
552
                }
 
553
                wpos = wordpos2str(WORD_POSN_INTERNAL, ignore_wpos);
 
554
                for (j=1;j<lnphns-1;j++){
 
555
                    basephone = wordarr[i]->phones[j];
 
556
                    phnptr = phninstall(basephone,lphnhash);
 
557
                    phnptr->count++;
 
558
                    if (IS_FILLER(basephone)) continue;
 
559
                    lctxt = wordarr[i]->phones[j-1];
 
560
                    if (IS_FILLER(lctxt)) lctxt = silencephn;
 
561
                    rctxt = wordarr[i]->phones[j+1];
 
562
                    if (IS_FILLER(rctxt)) rctxt = silencephn;
 
563
                    tphnptr = lookup(basephone,lctxt,rctxt,wpos,tphnhash);
 
564
                    (tphnptr->count)++;
 
565
                    niwdtphns++;
 
566
                }
 
567
 
 
568
                wpos = wordpos2str(WORD_POSN_END, ignore_wpos);
 
569
                basephone = wordarr[i]->phones[lnphns-1];
 
570
                phnptr = phninstall(basephone,lphnhash);
 
571
                phnptr->count++;
 
572
                if (IS_FILLER(basephone)) continue;
 
573
 
 
574
                lctxt = wordarr[i]->phones[lnphns-2];
 
575
                if (IS_FILLER(lctxt)) lctxt = silencephn;
 
576
                if (wordarr[i+1] != NULL){
 
577
                    rctxt = wordarr[i+1]->phones[0];
 
578
                    if (IS_FILLER(rctxt)) rctxt = silencephn;
 
579
                }
 
580
                else rctxt = silencephn;
 
581
                tphnptr = lookup(basephone,lctxt,rctxt,wpos,tphnhash);
 
582
                (tphnptr->count)++;
 
583
                newdtphns++;
 
584
            }
 
585
        }
 
586
        free(wordarr);
 
587
        free(tline);
 
588
    }
 
589
    fclose(fp);
 
590
    lineiter_free(line);
 
591
 
 
592
    *phnhash = lphnhash;
 
593
    E_INFO("%d words in transcripts\n",n_totalwds);
 
594
    E_INFO("%d single word triphones in transcripts\n",nswdtphns);
 
595
    E_INFO("%d word beginning triphones in transcripts\n",nbwdtphns);
 
596
    E_INFO("%d word internal triphones in transcripts\n",niwdtphns);
 
597
    E_INFO("%d word ending triphones in transcripts\n",newdtphns);
 
598
 
 
599
    return S3_SUCCESS;
 
600
}
 
601
 
 
602
 
 
603
int32 find_threshold(hashelement_t  **triphonehash)
 
604
{
 
605
    hashelement_t *triphone_el;
 
606
    int32 tottph, ltottph, mincnt, maxtph, *countofcounts, *lcountofcounts;
 
607
    int32 i, cnt, unique, threshold, ceiling;
 
608
 
 
609
    mincnt = cmd_ln_int32("-minocc");
 
610
    maxtph = cmd_ln_int32("-maxtriphones");
 
611
 
 
612
    ceiling = mincnt < CEILING ? CEILING : mincnt+1;
 
613
 
 
614
    tottph = ltottph = unique = 0;
 
615
    countofcounts = (int32 *) ckd_calloc(ceiling+1,sizeof(int32));
 
616
    lcountofcounts = (int32 *) ckd_calloc(ceiling+1,sizeof(int32));
 
617
    for (i = 0; i < HASHSIZE; i++){
 
618
        triphone_el = triphonehash[i];
 
619
        while (triphone_el != NULL){
 
620
            cnt = triphone_el->count;
 
621
            if (cnt > 0) {
 
622
                tottph += cnt;
 
623
                if (cnt >= mincnt) {
 
624
                    ltottph ++;
 
625
                    if (cnt > ceiling) cnt = ceiling;
 
626
                    lcountofcounts[cnt]++;
 
627
                }
 
628
                countofcounts[cnt]++; unique++;
 
629
            }
 
630
            triphone_el = triphone_el->next;
 
631
        }
 
632
    }
 
633
 
 
634
    threshold = mincnt; 
 
635
    while (ltottph > maxtph) {
 
636
        ltottph -= lcountofcounts[threshold];
 
637
        threshold++;
 
638
    }
 
639
 
 
640
    E_INFO("%d triphones extracted from transcripts\n",tottph);
 
641
    E_INFO("%d unique triphones extracted from transcripts\n",unique);
 
642
    E_INFO("%d triphones occur once in the transcripts\n",countofcounts[1]);
 
643
    E_INFO("%d triphones occur twice in the transcripts\n",countofcounts[2]);
 
644
    E_INFO("%d triphones occur thrice in the transcripts\n",countofcounts[3]);
 
645
    E_INFO("The rest of the triphones occur more than three times\n");
 
646
    E_INFO("Count threshold is %d\n",threshold);
 
647
 
 
648
    ckd_free(lcountofcounts); ckd_free(countofcounts);
 
649
    return(threshold);
 
650
}
 
651
 
 
652
 
 
653
int32 make_CD_heap(hashelement_t  **triphonehash,
 
654
                   int32  threshold,
 
655
                   heapelement_t ***CDheap,
 
656
                   int32  *cdheapsize)
 
657
{
 
658
    heapelement_t **heap=NULL, *addtriphone;
 
659
    hashelement_t *triphone_el;
 
660
    int32 i, heapsize;
 
661
 
 
662
    heapsize = 0;
 
663
    for (i = 0; i < HASHSIZE; i++){
 
664
        triphone_el = triphonehash[i];
 
665
        while (triphone_el != NULL){
 
666
            if (triphone_el->count >= threshold) {
 
667
                addtriphone = (heapelement_t *) calloc(1,sizeof(heapelement_t));
 
668
                if (addtriphone == NULL)
 
669
                    E_FATAL("Heap install error. Out of memory!\n");
 
670
                addtriphone->basephone = strdup(triphone_el->basephone);
 
671
                addtriphone->leftcontext = strdup(triphone_el->leftcontext);
 
672
                addtriphone->rightcontext = strdup(triphone_el->rightcontext);
 
673
                addtriphone->wordposition = strdup(triphone_el->wordposition);
 
674
                heapsize = insert(&heap, heapsize, addtriphone);
 
675
            }
 
676
            triphone_el = triphone_el->next;
 
677
        }
 
678
    }
 
679
    *CDheap = heap;
 
680
    *cdheapsize = heapsize;
 
681
 
 
682
    return S3_SUCCESS;
 
683
}
 
684
 
 
685
 
 
686
int32   print_counts(const char *countfn, phnhashelement_t  **CIhash,
 
687
                     hashelement_t **CDhash)
 
688
{
 
689
    heapelement_t **CDheap=NULL, **CIheap=NULL, *addphone, *addtriphone;
 
690
    hashelement_t *triphone_el; 
 
691
    phnhashelement_t *ciphone_el;
 
692
    int32 i, heapsize,cdheapsize,ciheapsize;
 
693
    FILE  *ofp;
 
694
 
 
695
    if ((ofp = fopen(countfn,"w")) == NULL){
 
696
        E_WARN("Unable to open %s for writing. Not writing counts!\n",countfn);
 
697
        return S3_ERROR;
 
698
    }
 
699
    fprintf(ofp,"base\tleft\tright\twdpos\tcount\n");
 
700
 
 
701
    ciheapsize = 0;
 
702
    for (i = 0; i < PHNHASHSIZE; i++){
 
703
        ciphone_el = CIhash[i];
 
704
        while (ciphone_el != NULL){
 
705
            if (ciphone_el->count < 1) {
 
706
                ciphone_el = ciphone_el->next;
 
707
                continue;
 
708
            }
 
709
            addphone = (heapelement_t *) ckd_calloc(1,sizeof(heapelement_t));
 
710
            addphone->basephone = strdup(ciphone_el->phone);
 
711
            addphone->count = ciphone_el->count;
 
712
            ciheapsize = insert(&CIheap, ciheapsize, addphone);
 
713
            ciphone_el = ciphone_el->next;
 
714
        }
 
715
    }
 
716
    heapsize = ciheapsize;
 
717
    for (i = 0; i < heapsize; i++) {
 
718
        addphone = yanktop(&CIheap,ciheapsize,&ciheapsize);
 
719
        fprintf(ofp,"%s\t-\t-\t-\t%d\n",addphone->basephone,addphone->count);
 
720
        free_heapelement(addphone);
 
721
    }
 
722
 
 
723
    cdheapsize = 0;
 
724
    for (i = 0; i < HASHSIZE; i++){
 
725
        triphone_el = CDhash[i];
 
726
        while (triphone_el != NULL){
 
727
            if (triphone_el->count < 1) {
 
728
                triphone_el = triphone_el->next;
 
729
                continue;
 
730
            }
 
731
            addtriphone = (heapelement_t *) ckd_calloc(1,sizeof(heapelement_t));
 
732
            addtriphone->basephone = strdup(triphone_el->basephone);
 
733
            addtriphone->leftcontext = strdup(triphone_el->leftcontext);
 
734
            addtriphone->rightcontext = strdup(triphone_el->rightcontext);
 
735
            addtriphone->wordposition = strdup(triphone_el->wordposition);
 
736
            addtriphone->count = triphone_el->count;
 
737
            cdheapsize = insert(&CDheap, cdheapsize, addtriphone);
 
738
            triphone_el = triphone_el->next;
 
739
        }
 
740
    }
 
741
    heapsize = cdheapsize;
 
742
    for (i = 0; i < heapsize; i++) {
 
743
        addtriphone = yanktop(&CDheap,cdheapsize,&cdheapsize);
 
744
        fprintf(ofp,"%s\t%s\t%s\t%s\t%d\n",addtriphone->basephone,
 
745
                                           addtriphone->leftcontext,
 
746
                                           addtriphone->rightcontext,
 
747
                                           addtriphone->wordposition,
 
748
                                           addtriphone->count);
 
749
        free_heapelement(addtriphone);
 
750
    }
 
751
    fclose(ofp);
 
752
    return S3_SUCCESS;
 
753
}
 
754
 
 
755
static const char* wordpos2str(word_posn_t wordpos, int ignore_wordpos)
 
756
{
 
757
    char *result;
 
758
 
 
759
    if (ignore_wordpos) {
 
760
        if (wordpos == WORD_POSN_UNDEFINED)
 
761
            return "-";
 
762
        return "i";
 
763
    }
 
764
 
 
765
    switch (wordpos) {
 
766
        case WORD_POSN_SINGLE: 
 
767
            result = "s";
 
768
            break;
 
769
        case WORD_POSN_BEGIN: 
 
770
            result = "b";
 
771
            break;
 
772
        case WORD_POSN_INTERNAL: 
 
773
            result = "i";
 
774
            break;
 
775
        case WORD_POSN_END: 
 
776
            result = "e";
 
777
            break;
 
778
        default:
 
779
            result = "-";
 
780
            break;
 
781
    }
 
782
    return result;
 
783
}
 
784
 
 
785
word_posn_t posnstr2wordpos(char *posn_str)
 
786
{
 
787
    if (posn_str == NULL) {
 
788
        E_WARN("No word position defined. Assuming word internal\n");
 
789
        return WORD_POSN_INTERNAL;
 
790
    }
 
791
    if (posn_str[0] == 's') return WORD_POSN_SINGLE;
 
792
    if (posn_str[0] == 'b') return WORD_POSN_BEGIN;
 
793
    if (posn_str[0] == 'i') return WORD_POSN_INTERNAL;
 
794
    if (posn_str[0] == 'e') return WORD_POSN_END;
 
795
    if (posn_str[0] == 'u') return WORD_POSN_UNDEFINED;
 
796
 
 
797
    E_WARN("unknown word position %s; assuming word internal\n", posn_str);
 
798
    return WORD_POSN_INTERNAL;
 
799
}
 
800
 
 
801
 
 
802
int32
 
803
output_model_def(FILE *fp,
 
804
                 char *pgm,
 
805
                 char **base_str,
 
806
                 char **left_str,
 
807
                 char **right_str,
 
808
                 char **posn_str,
 
809
                 uint32 *tmat,
 
810
                 uint32 **state,
 
811
                 uint32 n_base,
 
812
                 uint32 n_tri,
 
813
                 uint32 n_total,
 
814
                 uint32 n_state_pm,
 
815
                 uint32 n_tied_state,
 
816
                 uint32 n_tied_ci_state,
 
817
                 uint32 n_tied_tmat)
 
818
{
 
819
    time_t t;
 
820
    uint32 i, j;
 
821
    char *at;
 
822
 
 
823
    t = time(NULL);
 
824
    at = ctime((const time_t *)&t);
 
825
    at[strlen(at)-1] = '\0';
 
826
 
 
827
    fprintf(fp, "# Generated by %s on %s\n", pgm, at);
 
828
 
 
829
    fprintf(fp, "%s\n", MODEL_DEF_VERSION);
 
830
    fprintf(fp, "%u n_base\n", n_base);
 
831
    fprintf(fp, "%u n_tri\n", n_tri);
 
832
    fprintf(fp, "%u n_state_map\n", (n_base + n_tri) * n_state_pm);
 
833
    fprintf(fp, "%u n_tied_state\n", n_tied_state);
 
834
    fprintf(fp, "%u n_tied_ci_state\n", n_tied_ci_state);
 
835
    fprintf(fp, "%u n_tied_tmat\n", n_tied_tmat);
 
836
      
 
837
    fprintf(fp, "#\n# Columns definitions\n");
 
838
    fprintf(fp, "#%4s %3s %3s %1s %6s %4s %s\n",
 
839
            "base", "lft", "rt", "p", "attrib", "tmat",
 
840
            "     ... state id's ...");
 
841
    for (i = 0; i < n_total; i++) {
 
842
        if ((base_str[i][0] == '+') ||
 
843
            (strncmp(base_str[i], "SIL", 3) == 0)) {
 
844
            fprintf(fp, "%5s %3s %3s %1s %6s %4d",
 
845
                    base_str[i],
 
846
                    (left_str[i] != NULL ? left_str[i] : "-"),
 
847
                    (right_str[i] != NULL ? right_str[i] : "-"),
 
848
                    (posn_str[i] != NULL ? posn_str[i] : "-"),
 
849
                    "filler",
 
850
                    tmat[i]);
 
851
        }
 
852
        else {
 
853
            fprintf(fp, "%5s %3s %3s %1s %6s %4d",
 
854
                    base_str[i],
 
855
                    (left_str[i] != NULL ? left_str[i] : "-"),
 
856
                    (right_str[i] != NULL ? right_str[i] : "-"),
 
857
                    (posn_str[i] != NULL ? posn_str[i] : "-"),
 
858
                    "n/a",
 
859
                    tmat[i]);
 
860
        }
 
861
            
 
862
 
 
863
        for (j = 0; j < n_state_pm-1; j++) {
 
864
            fprintf(fp, " %4u", state[i][j]);
 
865
        }
 
866
        fprintf(fp, "    N\n");
 
867
    }
 
868
    return S3_SUCCESS;
 
869
}
 
870
 
 
871
 
 
872
int32 make_mdef_from_list(const char *mdeffile,
 
873
                        char **CIlist, 
 
874
                        int32  nciphones,
 
875
                        heapelement_t **CDheap,
 
876
                        int32  cdheapsize,
 
877
                        char  *pgm)
 
878
{
 
879
    int32  n_base, n_tri, n_state_pm, n_total;
 
880
    int32  n_tied_state_ci, n_tied_state_cd, n_tied_tmat;
 
881
    char   **base_str, **left_str, **right_str, **posn_str;
 
882
    acmod_id_t base, left, right;
 
883
    word_posn_t posn;
 
884
    acmod_set_t  *acmod_set;
 
885
    int32 i,j,k;
 
886
    const char *filler_attr[] = {"filler", NULL};
 
887
    const char *base_attr[] = {"base", NULL};
 
888
    const char *na_attr[] = {"n/a", NULL};
 
889
    uint32 *tmat; 
 
890
    uint32 **smap;
 
891
    heapelement_t *cdphone;
 
892
    FILE  *fp;
 
893
 
 
894
    n_state_pm = cmd_ln_int32("-n_state_pm") + 1;
 
895
    n_base = nciphones;
 
896
    n_tri = cdheapsize;
 
897
    E_INFO("%d n_base, %d n_tri\n", n_base, n_tri);
 
898
 
 
899
    n_tied_state_ci = n_base * (n_state_pm-1);
 
900
    n_tied_state_cd = n_tri * (n_state_pm-1);
 
901
    n_tied_tmat = n_base;
 
902
 
 
903
    n_total = n_base + n_tri;
 
904
    base_str = ckd_calloc(n_total, sizeof(char *));
 
905
    left_str = ckd_calloc(n_total, sizeof(char *));
 
906
    right_str = ckd_calloc(n_total, sizeof(char *));
 
907
    posn_str = ckd_calloc(n_total, sizeof(char *));
 
908
 
 
909
    tmat = ckd_calloc(n_total, sizeof(uint32));
 
910
 
 
911
    smap = (uint32 **)ckd_calloc_2d(n_total, n_state_pm-1, sizeof(uint32));
 
912
 
 
913
    for (i = 0; i < n_base; i++) {
 
914
        base_str[i] = strdup(CIlist[i]);
 
915
    }
 
916
 
 
917
    for (j = 0; j < n_tri; j++,i++) {
 
918
        cdphone = yanktop(&CDheap,cdheapsize,&cdheapsize);
 
919
        base_str[i] = strdup(cdphone->basephone);
 
920
        left_str[i] = strdup(cdphone->leftcontext);
 
921
        right_str[i] = strdup(cdphone->rightcontext);
 
922
        posn_str[i] = strdup(cdphone->wordposition);
 
923
        free_heapelement(cdphone);
 
924
    }
 
925
    if (cdheapsize != 0) 
 
926
        E_FATAL("Error in CD heap! Error in dictionary or phone list!\n");
 
927
 
 
928
    for (i = 0, k = 0; i < n_total; i++)
 
929
        for (j = 0; j < n_state_pm-1; j++)
 
930
            smap[i][j] = k++;
 
931
 
 
932
    acmod_set = acmod_set_new();
 
933
 
 
934
    acmod_set_set_n_ci_hint(acmod_set, n_base);
 
935
    acmod_set_set_n_tri_hint(acmod_set, n_tri);
 
936
 
 
937
    for (i = 0; i < n_base; i++) {
 
938
        if (base_str[i][0] == '+') {
 
939
            base = acmod_set_add_ci(acmod_set, base_str[i], filler_attr);
 
940
        }
 
941
        else {
 
942
            base = acmod_set_add_ci(acmod_set, base_str[i], base_attr);
 
943
        }
 
944
        tmat[base] = base;
 
945
    }
 
946
 
 
947
    for (; i < n_total; i++) {
 
948
        if ((base = acmod_set_name2id(acmod_set, base_str[i])) == NO_ACMOD)
 
949
            E_FATAL("Error in dictionary or phonelist. Bad basephone %s in triphone list!\n",base_str[i]);
 
950
        if ((left = acmod_set_name2id(acmod_set, left_str[i])) == NO_ACMOD)
 
951
            E_FATAL("Error in dictionary or phonelist. Bad leftphone %s in triphone list!\n",left_str[i]);
 
952
        if ((right = acmod_set_name2id(acmod_set, right_str[i])) == NO_ACMOD)
 
953
            E_FATAL("Error in dictionary or phonelist. Bad rightphone %s in triphone list!\n",right_str[i]);
 
954
 
 
955
        posn = posnstr2wordpos(posn_str[i]);
 
956
 
 
957
        acmod_set_add_tri(acmod_set, base, left, right, posn, na_attr);
 
958
        tmat[i] = base;
 
959
    }
 
960
 
 
961
    if ((fp = fopen(mdeffile, "w")) == NULL)
 
962
        E_FATAL("Unable to open %s for writing!\n",mdeffile);
 
963
    output_model_def(fp,
 
964
             pgm,
 
965
             base_str, left_str, right_str, posn_str, tmat, smap,
 
966
             n_base, n_tri, n_total, n_state_pm,
 
967
             n_tied_state_ci+n_tied_state_cd, n_tied_state_ci, n_tied_tmat);
 
968
    fclose(fp);
 
969
    E_INFO("Wrote mdef file %s\n",mdeffile);
 
970
 
 
971
    for (j = 0; j < n_tri; j++,i++) {
 
972
        if (base_str[j] != NULL) free(base_str[j]);
 
973
        if (left_str[j] != NULL) free(left_str[j]);
 
974
        if (right_str[j] != NULL) free(right_str[j]);
 
975
        if (posn_str[j] != NULL) free(posn_str[j]);
 
976
    }
 
977
    free(base_str); free(left_str); free(right_str); free(posn_str);
 
978
    ckd_free_2d((void**)smap);
 
979
 
 
980
    return 0;
 
981
}