1 /* -*- c-basic-offset:4; indent-tabs-mode: nil -*- */
2 /* ====================================================================
3  * Copyright (c) 1999-2004 Carnegie Mellon University.  All rights
4  * reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  *
18  * This work was supported in part by funding from the Defense Advanced
19  * Research Projects Agency and the National Science Foundation of the
20  * United States of America, and the CMU Sphinx Speech Consortium.
21  *
22  * THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND
23  * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
24  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY
26  * NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  * ====================================================================
35  *
36  */
37 
38 #include <string.h>
39 
40 #include "dict2pid.h"
41 #include "hmm.h"
42 
43 
44 /**
45  * @file dict2pid.c - dictionary word to senone sequence mappings
46  */
47 
48 void
compress_table(s3ssid_t * uncomp_tab,s3ssid_t * com_tab,s3cipid_t * ci_map,int32 n_ci)49 compress_table(s3ssid_t * uncomp_tab, s3ssid_t * com_tab,
50                s3cipid_t * ci_map, int32 n_ci)
51 {
52     int32 found;
53     int32 r;
54     int32 tmp_r;
55 
56     for (r = 0; r < n_ci; r++) {
57         com_tab[r] = BAD_S3SSID;
58         ci_map[r] = BAD_S3CIPID;
59     }
60     /** Compress this map */
61     for (r = 0; r < n_ci; r++) {
62 
63         found = 0;
64         for (tmp_r = 0; tmp_r < r && com_tab[tmp_r] != BAD_S3SSID; tmp_r++) {   /* If it appears before, just filled in cimap; */
65             if (uncomp_tab[r] == com_tab[tmp_r]) {
66                 found = 1;
67                 ci_map[r] = tmp_r;
68                 break;
69             }
70         }
71 
72         if (found == 0) {
73             com_tab[tmp_r] = uncomp_tab[r];
74             ci_map[r] = tmp_r;
75         }
76     }
77 }
78 
79 
80 static void
compress_right_context_tree(dict2pid_t * d2p,s3ssid_t *** rdiph_rc)81 compress_right_context_tree(dict2pid_t * d2p,
82                             s3ssid_t ***rdiph_rc)
83 {
84     int32 n_ci;
85     int32 b, l, r;
86     s3ssid_t *rmap;
87     s3ssid_t *tmpssid;
88     s3cipid_t *tmpcimap;
89     bin_mdef_t *mdef = d2p->mdef;
90     size_t alloc;
91 
92     n_ci = mdef->n_ciphone;
93 
94     tmpssid = ckd_calloc(n_ci, sizeof(s3ssid_t));
95     tmpcimap = ckd_calloc(n_ci, sizeof(s3cipid_t));
96 
97     d2p->rssid =
98         (xwdssid_t **) ckd_calloc(mdef->n_ciphone, sizeof(xwdssid_t *));
99     alloc = mdef->n_ciphone * sizeof(xwdssid_t *);
100 
101     for (b = 0; b < n_ci; b++) {
102         d2p->rssid[b] =
103             (xwdssid_t *) ckd_calloc(mdef->n_ciphone, sizeof(xwdssid_t));
104         alloc += mdef->n_ciphone * sizeof(xwdssid_t);
105 
106         for (l = 0; l < n_ci; l++) {
107             rmap = rdiph_rc[b][l];
108             compress_table(rmap, tmpssid, tmpcimap, mdef->n_ciphone);
109 
110             for (r = 0; r < mdef->n_ciphone && tmpssid[r] != BAD_S3SSID;
111                  r++);
112 
113             if (tmpssid[0] != BAD_S3SSID) {
114                 d2p->rssid[b][l].ssid = ckd_calloc(r, sizeof(s3ssid_t));
115                 memcpy(d2p->rssid[b][l].ssid, tmpssid,
116                        r * sizeof(s3ssid_t));
117                 d2p->rssid[b][l].cimap =
118                     ckd_calloc(mdef->n_ciphone, sizeof(s3cipid_t));
119                 memcpy(d2p->rssid[b][l].cimap, tmpcimap,
120                        (mdef->n_ciphone) * sizeof(s3cipid_t));
121                 d2p->rssid[b][l].n_ssid = r;
122             }
123             else {
124                 d2p->rssid[b][l].ssid = NULL;
125                 d2p->rssid[b][l].cimap = NULL;
126                 d2p->rssid[b][l].n_ssid = 0;
127             }
128         }
129     }
130 
131     E_INFO("Allocated %d bytes (%d KiB) for word-final triphones\n",
132            (int)alloc, (int)alloc / 1024);
133     ckd_free(tmpssid);
134     ckd_free(tmpcimap);
135 }
136 
137 static void
compress_left_right_context_tree(dict2pid_t * d2p)138 compress_left_right_context_tree(dict2pid_t * d2p)
139 {
140     int32 n_ci;
141     int32 b, l, r;
142     s3ssid_t *rmap;
143     s3ssid_t *tmpssid;
144     s3cipid_t *tmpcimap;
145     bin_mdef_t *mdef = d2p->mdef;
146     size_t alloc;
147 
148     n_ci = mdef->n_ciphone;
149 
150     tmpssid = ckd_calloc(n_ci, sizeof(s3ssid_t));
151     tmpcimap = ckd_calloc(n_ci, sizeof(s3cipid_t));
152 
153     assert(d2p->lrdiph_rc);
154 
155     d2p->lrssid =
156         (xwdssid_t **) ckd_calloc(mdef->n_ciphone, sizeof(xwdssid_t *));
157     alloc = mdef->n_ciphone * sizeof(xwdssid_t *);
158 
159     for (b = 0; b < n_ci; b++) {
160 
161         d2p->lrssid[b] =
162             (xwdssid_t *) ckd_calloc(mdef->n_ciphone, sizeof(xwdssid_t));
163         alloc += mdef->n_ciphone * sizeof(xwdssid_t);
164 
165         for (l = 0; l < n_ci; l++) {
166             rmap = d2p->lrdiph_rc[b][l];
167 
168             compress_table(rmap, tmpssid, tmpcimap, mdef->n_ciphone);
169 
170             for (r = 0; r < mdef->n_ciphone && tmpssid[r] != BAD_S3SSID;
171                  r++);
172 
173             if (tmpssid[0] != BAD_S3SSID) {
174                 d2p->lrssid[b][l].ssid = ckd_calloc(r, sizeof(s3ssid_t));
175                 memcpy(d2p->lrssid[b][l].ssid, tmpssid,
176                        r * sizeof(s3ssid_t));
177                 d2p->lrssid[b][l].cimap =
178                     ckd_calloc(mdef->n_ciphone, sizeof(s3cipid_t));
179                 memcpy(d2p->lrssid[b][l].cimap, tmpcimap,
180                        (mdef->n_ciphone) * sizeof(s3cipid_t));
181                 d2p->lrssid[b][l].n_ssid = r;
182             }
183             else {
184                 d2p->lrssid[b][l].ssid = NULL;
185                 d2p->lrssid[b][l].cimap = NULL;
186                 d2p->lrssid[b][l].n_ssid = 0;
187             }
188         }
189     }
190 
191     /* Try to compress lrdiph_rc into lrdiph_rc_compressed */
192     ckd_free(tmpssid);
193     ckd_free(tmpcimap);
194 
195     E_INFO("Allocated %d bytes (%d KiB) for single-phone word triphones\n",
196            (int)alloc, (int)alloc / 1024);
197 }
198 
199 /**
200    ARCHAN, A duplicate of get_rc_npid in ctxt_table.h.  I doubt whether it is correct
201    because the compressed map has not been checked.
202 */
203 int32
get_rc_nssid(dict2pid_t * d2p,s3wid_t w)204 get_rc_nssid(dict2pid_t * d2p, s3wid_t w)
205 {
206     int32 pronlen;
207     s3cipid_t b, lc;
208     dict_t *dict = d2p->dict;
209 
210     pronlen = dict->word[w].pronlen;
211     b = dict->word[w].ciphone[pronlen - 1];
212 
213     if (pronlen == 1) {
214         /* Is this true ?
215            No known left context.  But all cimaps (for any l) are identical; pick one
216         */
217         /*E_INFO("Single phone word\n"); */
218         return (d2p->lrssid[b][0].n_ssid);
219     }
220     else {
221         /*    E_INFO("Multiple phone word\n"); */
222         lc = dict->word[w].ciphone[pronlen - 2];
223         return (d2p->rssid[b][lc].n_ssid);
224     }
225 
226 }
227 
228 s3cipid_t *
dict2pid_get_rcmap(dict2pid_t * d2p,s3wid_t w)229 dict2pid_get_rcmap(dict2pid_t * d2p, s3wid_t w)
230 {
231     int32 pronlen;
232     s3cipid_t b, lc;
233     dict_t *dict = d2p->dict;
234 
235     pronlen = dict->word[w].pronlen;
236     b = dict->word[w].ciphone[pronlen - 1];
237 
238     if (pronlen == 1) {
239         /* Is this true ?
240            No known left context.  But all cimaps (for any l) are identical; pick one
241         */
242         /*E_INFO("Single phone word\n"); */
243         return (d2p->lrssid[b][0].cimap);
244     }
245     else {
246         /*    E_INFO("Multiple phone word\n"); */
247         lc = dict->word[w].ciphone[pronlen - 2];
248         return (d2p->rssid[b][lc].cimap);
249     }
250 }
251 
252 static void
free_compress_map(xwdssid_t ** tree,int32 n_ci)253 free_compress_map(xwdssid_t ** tree, int32 n_ci)
254 {
255     int32 b, l;
256     for (b = 0; b < n_ci; b++) {
257         for (l = 0; l < n_ci; l++) {
258             ckd_free(tree[b][l].ssid);
259             ckd_free(tree[b][l].cimap);
260         }
261         ckd_free(tree[b]);
262     }
263     ckd_free(tree);
264 }
265 
266 static void
populate_lrdiph(dict2pid_t * d2p,s3ssid_t *** rdiph_rc,s3cipid_t b)267 populate_lrdiph(dict2pid_t *d2p, s3ssid_t ***rdiph_rc, s3cipid_t b)
268 {
269     bin_mdef_t *mdef = d2p->mdef;
270     s3cipid_t l, r;
271 
272     for (l = 0; l < bin_mdef_n_ciphone(mdef); l++) {
273         for (r = 0; r < bin_mdef_n_ciphone(mdef); r++) {
274             s3pid_t p;
275             p = bin_mdef_phone_id_nearest(mdef, (s3cipid_t) b,
276                                           (s3cipid_t) l,
277                                           (s3cipid_t) r,
278                                           WORD_POSN_SINGLE);
279             d2p->lrdiph_rc[b][l][r]
280                 = bin_mdef_pid2ssid(mdef, p);
281             if (r == bin_mdef_silphone(mdef))
282                 d2p->ldiph_lc[b][r][l]
283                     = bin_mdef_pid2ssid(mdef, p);
284             if (rdiph_rc && l == bin_mdef_silphone(mdef))
285                 rdiph_rc[b][l][r]
286                     = bin_mdef_pid2ssid(mdef, p);
287             assert(IS_S3SSID(bin_mdef_pid2ssid(mdef, p)));
288             E_DEBUG(2,("%s(%s,%s) => %d / %d\n",
289                        bin_mdef_ciphone_str(mdef, b),
290                        bin_mdef_ciphone_str(mdef, l),
291                        bin_mdef_ciphone_str(mdef, r),
292                        p, bin_mdef_pid2ssid(mdef, p)));
293         }
294     }
295 }
296 
297 int
dict2pid_add_word(dict2pid_t * d2p,int32 wid)298 dict2pid_add_word(dict2pid_t *d2p,
299                   int32 wid)
300 {
301     bin_mdef_t *mdef = d2p->mdef;
302     dict_t *d = d2p->dict;
303 
304     if (dict_pronlen(d, wid) > 1) {
305         s3cipid_t l;
306         /* Make sure we have left and right context diphones for this
307          * word. */
308         if (d2p->ldiph_lc[dict_first_phone(d, wid)][dict_second_phone(d, wid)][0]
309             == BAD_S3SSID) {
310             E_DEBUG(2, ("Filling in left-context diphones for %s(?,%s)\n",
311                    bin_mdef_ciphone_str(mdef, dict_first_phone(d, wid)),
312                    bin_mdef_ciphone_str(mdef, dict_second_phone(d, wid))));
313             for (l = 0; l < bin_mdef_n_ciphone(mdef); l++) {
314                 int p
315                     = bin_mdef_phone_id_nearest(mdef,
316                                                 dict_first_phone(d, wid), l,
317                                                 dict_second_phone(d, wid),
318                                                 WORD_POSN_BEGIN);
319                 d2p->ldiph_lc[dict_first_phone(d, wid)][dict_second_phone(d, wid)][l]
320                     = bin_mdef_pid2ssid(mdef, p);
321             }
322         }
323         if (d2p->rssid[dict_last_phone(d, wid)][dict_second_last_phone(d, wid)].n_ssid
324             == 0) {
325             s3ssid_t *rmap;
326             s3ssid_t *tmpssid;
327             s3cipid_t *tmpcimap;
328             s3cipid_t r;
329 
330             E_DEBUG(2, ("Filling in right-context diphones for %s(%s,?)\n",
331                    bin_mdef_ciphone_str(mdef, dict_last_phone(d, wid)),
332                    bin_mdef_ciphone_str(mdef, dict_second_last_phone(d, wid))));
333             rmap = ckd_calloc(bin_mdef_n_ciphone(mdef), sizeof(*rmap));
334             for (r = 0; r < bin_mdef_n_ciphone(mdef); r++) {
335                 int p
336                     = bin_mdef_phone_id_nearest(mdef,
337                                                 dict_last_phone(d, wid),
338                                                 dict_second_last_phone(d, wid), r,
339                                                 WORD_POSN_END);
340                 rmap[r] = bin_mdef_pid2ssid(mdef, p);
341             }
342             tmpssid = ckd_calloc(bin_mdef_n_ciphone(mdef), sizeof(*tmpssid));
343             tmpcimap = ckd_calloc(bin_mdef_n_ciphone(mdef), sizeof(*tmpcimap));
344             compress_table(rmap, tmpssid, tmpcimap, bin_mdef_n_ciphone(mdef));
345             for (r = 0; r < mdef->n_ciphone && tmpssid[r] != BAD_S3SSID; r++)
346                 ;
347             d2p->rssid[dict_last_phone(d, wid)][dict_second_last_phone(d, wid)].ssid = tmpssid;
348             d2p->rssid[dict_last_phone(d, wid)][dict_second_last_phone(d, wid)].cimap = tmpcimap;
349             d2p->rssid[dict_last_phone(d, wid)][dict_second_last_phone(d, wid)].n_ssid = r;
350             ckd_free(rmap);
351         }
352     }
353     else {
354         /* Make sure we have a left-right context triphone entry for
355          * this word. */
356         E_INFO("Filling in context triphones for %s(?,?)\n",
357                bin_mdef_ciphone_str(mdef, dict_first_phone(d, wid)));
358         if (d2p->lrdiph_rc[dict_first_phone(d, wid)][0][0] == BAD_S3SSID) {
359             populate_lrdiph(d2p, NULL, dict_first_phone(d, wid));
360         }
361     }
362 
363     return 0;
364 }
365 
366 s3ssid_t
dict2pid_internal(dict2pid_t * d2p,int32 wid,int pos)367 dict2pid_internal(dict2pid_t *d2p,
368                   int32 wid,
369                   int pos)
370 {
371     int b, l, r, p;
372     dict_t *dict = d2p->dict;
373     bin_mdef_t *mdef = d2p->mdef;
374 
375     if (pos == 0 || pos == dict_pronlen(dict, wid))
376         return BAD_S3SSID;
377 
378     b = dict_pron(dict, wid, pos);
379     l = dict_pron(dict, wid, pos - 1);
380     r = dict_pron(dict, wid, pos + 1);
381     p = bin_mdef_phone_id_nearest(mdef, (s3cipid_t) b,
382                                   (s3cipid_t) l, (s3cipid_t) r,
383                                   WORD_POSN_INTERNAL);
384     return bin_mdef_pid2ssid(mdef, p);
385 }
386 
387 dict2pid_t *
dict2pid_build(bin_mdef_t * mdef,dict_t * dict)388 dict2pid_build(bin_mdef_t * mdef, dict_t * dict)
389 {
390     dict2pid_t *dict2pid;
391     s3ssid_t ***rdiph_rc;
392     bitvec_t *ldiph, *rdiph, *single;
393     int32 pronlen;
394     int32 b, l, r, w, p;
395 
396     E_INFO("Building PID tables for dictionary\n");
397     assert(mdef);
398     assert(dict);
399 
400     dict2pid = (dict2pid_t *) ckd_calloc(1, sizeof(dict2pid_t));
401     dict2pid->refcount = 1;
402     dict2pid->mdef = bin_mdef_retain(mdef);
403     dict2pid->dict = dict_retain(dict);
404     E_INFO("Allocating %d^3 * %d bytes (%d KiB) for word-initial triphones\n",
405            mdef->n_ciphone, sizeof(s3ssid_t),
406            mdef->n_ciphone * mdef->n_ciphone * mdef->n_ciphone * sizeof(s3ssid_t) / 1024);
407     dict2pid->ldiph_lc =
408         (s3ssid_t ***) ckd_calloc_3d(mdef->n_ciphone, mdef->n_ciphone,
409                                      mdef->n_ciphone, sizeof(s3ssid_t));
410     /* Only used internally to generate rssid */
411     rdiph_rc =
412         (s3ssid_t ***) ckd_calloc_3d(mdef->n_ciphone, mdef->n_ciphone,
413                                      mdef->n_ciphone, sizeof(s3ssid_t));
414 
415     dict2pid->lrdiph_rc = (s3ssid_t ***) ckd_calloc_3d(mdef->n_ciphone,
416                                                        mdef->n_ciphone,
417                                                        mdef->n_ciphone,
418                                                        sizeof
419                                                        (s3ssid_t));
420     /* Actually could use memset for this, if BAD_S3SSID is guaranteed
421      * to be 65535... */
422     for (b = 0; b < mdef->n_ciphone; ++b) {
423         for (r = 0; r < mdef->n_ciphone; ++r) {
424             for (l = 0; l < mdef->n_ciphone; ++l) {
425                 dict2pid->ldiph_lc[b][r][l] = BAD_S3SSID;
426                 dict2pid->lrdiph_rc[b][l][r] = BAD_S3SSID;
427                 rdiph_rc[b][l][r] = BAD_S3SSID;
428             }
429         }
430     }
431 
432     /* Track which diphones / ciphones have been seen. */
433     ldiph = bitvec_alloc(mdef->n_ciphone * mdef->n_ciphone);
434     rdiph = bitvec_alloc(mdef->n_ciphone * mdef->n_ciphone);
435     single = bitvec_alloc(mdef->n_ciphone);
436 
437     for (w = 0; w < dict_size(dict2pid->dict); w++) {
438         pronlen = dict_pronlen(dict, w);
439 
440         if (pronlen >= 2) {
441             b = dict_first_phone(dict, w);
442             r = dict_second_phone(dict, w);
443             /* Populate ldiph_lc */
444             if (bitvec_is_clear(ldiph, b * mdef->n_ciphone + r)) {
445                 /* Mark this diphone as done */
446                 bitvec_set(ldiph, b * mdef->n_ciphone + r);
447 
448                 /* Record all possible ssids for b(?,r) */
449                 for (l = 0; l < bin_mdef_n_ciphone(mdef); l++) {
450                     p = bin_mdef_phone_id_nearest(mdef, (s3cipid_t) b,
451                                               (s3cipid_t) l, (s3cipid_t) r,
452                                               WORD_POSN_BEGIN);
453                     dict2pid->ldiph_lc[b][r][l] = bin_mdef_pid2ssid(mdef, p);
454                 }
455             }
456 
457 
458             /* Populate rdiph_rc */
459             l = dict_second_last_phone(dict, w);
460             b = dict_last_phone(dict, w);
461             if (bitvec_is_clear(rdiph, b * mdef->n_ciphone + l)) {
462                 /* Mark this diphone as done */
463                 bitvec_set(rdiph, b * mdef->n_ciphone + l);
464 
465                 for (r = 0; r < bin_mdef_n_ciphone(mdef); r++) {
466                     p = bin_mdef_phone_id_nearest(mdef, (s3cipid_t) b,
467                                               (s3cipid_t) l, (s3cipid_t) r,
468                                               WORD_POSN_END);
469                     rdiph_rc[b][l][r] = bin_mdef_pid2ssid(mdef, p);
470                 }
471             }
472         }
473         else if (pronlen == 1) {
474             b = dict_pron(dict, w, 0);
475             E_DEBUG(1,("Building tables for single phone word %s phone %d = %s\n",
476                        dict_wordstr(dict, w), b, bin_mdef_ciphone_str(mdef, b)));
477             /* Populate lrdiph_rc (and also ldiph_lc, rdiph_rc if needed) */
478             if (bitvec_is_clear(single, b)) {
479                 populate_lrdiph(dict2pid, rdiph_rc, b);
480                 bitvec_set(single, b);
481             }
482         }
483     }
484 
485     bitvec_free(ldiph);
486     bitvec_free(rdiph);
487     bitvec_free(single);
488 
489     /* Try to compress rdiph_rc into rdiph_rc_compressed */
490     compress_right_context_tree(dict2pid, rdiph_rc);
491     compress_left_right_context_tree(dict2pid);
492 
493     ckd_free_3d(rdiph_rc);
494 
495     dict2pid_report(dict2pid);
496     return dict2pid;
497 }
498 
499 dict2pid_t *
dict2pid_retain(dict2pid_t * d2p)500 dict2pid_retain(dict2pid_t *d2p)
501 {
502     ++d2p->refcount;
503     return d2p;
504 }
505 
506 int
dict2pid_free(dict2pid_t * d2p)507 dict2pid_free(dict2pid_t * d2p)
508 {
509     if (d2p == NULL)
510         return 0;
511     if (--d2p->refcount > 0)
512         return d2p->refcount;
513 
514     if (d2p->ldiph_lc)
515         ckd_free_3d((void ***) d2p->ldiph_lc);
516 
517     if (d2p->lrdiph_rc)
518         ckd_free_3d((void ***) d2p->lrdiph_rc);
519 
520     if (d2p->rssid)
521         free_compress_map(d2p->rssid, bin_mdef_n_ciphone(d2p->mdef));
522 
523     if (d2p->lrssid)
524         free_compress_map(d2p->lrssid, bin_mdef_n_ciphone(d2p->mdef));
525 
526     bin_mdef_free(d2p->mdef);
527     dict_free(d2p->dict);
528     ckd_free(d2p);
529     return 0;
530 }
531 
532 void
dict2pid_report(dict2pid_t * d2p)533 dict2pid_report(dict2pid_t * d2p)
534 {
535 }
536 
537 void
dict2pid_dump(FILE * fp,dict2pid_t * d2p)538 dict2pid_dump(FILE * fp, dict2pid_t * d2p)
539 {
540     int32 w, p, pronlen;
541     int32 i, j, b, l, r;
542     bin_mdef_t *mdef = d2p->mdef;
543     dict_t *dict = d2p->dict;
544 
545     fprintf(fp, "# INTERNAL (wd comssid ssid ssid ... ssid comssid)\n");
546     for (w = 0; w < dict_size(dict); w++) {
547         fprintf(fp, "%30s ", dict_wordstr(dict, w));
548 
549         pronlen = dict_pronlen(dict, w);
550         for (p = 0; p < pronlen; p++)
551             fprintf(fp, " %5d", dict2pid_internal(d2p, w, p));
552         fprintf(fp, "\n");
553     }
554     fprintf(fp, "#\n");
555 
556     fprintf(fp, "# LDIPH_LC (b r l ssid)\n");
557     for (b = 0; b < bin_mdef_n_ciphone(mdef); b++) {
558         for (r = 0; r < bin_mdef_n_ciphone(mdef); r++) {
559             for (l = 0; l < bin_mdef_n_ciphone(mdef); l++) {
560                 if (IS_S3SSID(d2p->ldiph_lc[b][r][l]))
561                     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 */
562             }
563         }
564     }
565     fprintf(fp, "#\n");
566 
567     fprintf(fp, "# SSEQ %d (senid senid ...)\n", mdef->n_sseq);
568     for (i = 0; i < mdef->n_sseq; i++) {
569         fprintf(fp, "%5d ", i);
570         for (j = 0; j < bin_mdef_n_emit_state(mdef); j++)
571             fprintf(fp, " %5d", mdef->sseq[i][j]);
572         fprintf(fp, "\n");
573     }
574     fprintf(fp, "#\n");
575     fprintf(fp, "# END\n");
576 
577     fflush(fp);
578 }
579