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