1 /*
2 * Copyright (C) 2008-2009 SVOX AG, Baslerstr. 30, 8048 Zuerich, Switzerland
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 /**
17 * @file picospho.c
18 *
19 * sentence phonemic/phonetic FSTs PU
20 *
21 * Copyright (C) 2008-2009 SVOX AG, Baslerstr. 30, 8048 Zuerich, Switzerland
22 * All rights reserved.
23 *
24 * History:
25 * - 2009-04-20 -- initial version
26 *
27 */
28
29 #include "picoos.h"
30 #include "picodbg.h"
31 #include "picodata.h"
32
33 #include "picoknow.h"
34 #include "picokfst.h"
35 #include "picoktab.h"
36 #include "picotrns.h"
37
38 #include "picospho.h"
39
40 #ifdef __cplusplus
41 extern "C" {
42 #endif
43 #if 0
44 }
45 #endif
46
47 #define SPHO_BUFSIZE (3 * PICODATA_BUFSIZE_DEFAULT)
48
49
50
51 #define SPHO_MAX_ALTDESC_SIZE (60 * PICOTRNS_MAX_NUM_POSSYM)
52
53
54 #define SPHO_SMALLEST_SIL_DUR 1
55
56
57 /** @addtogroup picospho
58 *
59 * Algorithmic description
60 * =======================
61 * The main function, sphoStep, is divided into the subprocesses (processing states) described further down.
62 *
63 * Flow control:
64 * ------------
65 * The processing flow is controlled by setting
66 * - 'procState' : the next state to be processed
67 * - 'feedFollowState' : the state to be processed after the feed state (the feed state is treated like a primitive "subroutine")
68 * - some other flags
69 *
70 * Buffering:
71 * ---------
72 * - The input items are mainly stored and processed in two buffers, collectively called 'inBuf'
73 * - cbuf : unstructured buffer containing item contents
74 * - headx : structured buffer containing item heads, each expanded by a pointer to the item contents
75 * and space for a boundary potentially to be inserted (to the left of the original item)
76 * - For transduction, phonemes and their position are extracted from inBuf into
77 * - phonBuf,
78 * processed there, and the resulting phonemes realigned with inBuf.
79 * - Word items are split into syllables, stored in
80 * - sylBuf
81 * - Items to be output are stored in outBuf
82 *
83 * Windowing:
84 * ---------
85 * Optimal solutions are achieved if a whole sentence is processed at once. However, if any of the buffers are too small,
86 * only sentence parts are processed. To improve the quality of such sub-optimal solutions, a moving-window-with-overlap is applied:
87 * - [0,headxReadPos[ : the window considered for transduction
88 * - [activeStartPos,activeEndPos[ : the "active" subrange of the window actually used for output
89 * - penultima : the position (within the active range) that should be used as new window start when shifting the window
90 *
91 * After PROCESS_PARSE:
92 * 0 activeStartPos penultima activeEndPos headxReadPos headxWritePos
93 * | | | | | |
94 * |-------------=================================---------------| ['----': context '====' : active subrange)
95 *
96 * After PROCESS_SHIFT:
97 * 0 activeStartPos headWritePos
98 * | | | |
99 * |------------... (only left context is known; new active range, penultima, and right context to be established at next parse)
100 *
101 * Processing states:
102 * -----------------
103 * - INIT : initialize state variables
104 * - COLLECT : collect items into internal buffers ("inBuf")
105 * - PROCESS_PARSE : go through inBuf items and extract position/phoneme pairs into phoneme buffer 'phonBuf'
106 * word boundary phonemes are inserted between words
107 * - PROCESS_TRANSDUCE : transduce phonBuf
108 * - PROCESS_BOUNDS : go through inBuf items again and match against transduced pos/phoneme
109 * this is the first round of alignment, only inserting/deleting/modifying bounds, according to
110 * - existing BOUND items
111 * - newly produced word bounds separating WORDPHON items
112 * - bound upgrades/downgrades from transduction
113 * - bound upgrades/downgrades/insertions from SIL command items (originating e.g. from <break> text commands)
114 * all relevant bounds are placed in the corresponding headx extention; original bound items become invalid.
115 * - PROCESS_RECOMB : go through inBuf items again and match against transduced pos/phoneme
116 * this is the second round of alignment, treating non-BOUND items
117 * - WORDPHONs are broken into syllables by "calling" PROCESS_SYL
118 * - "side-bounds" (in the headx extension) are output by "calling" FEED
119 * - BOUND items are consumed with no effect
120 * - other items are output unchanged "calling" FEED
121 * - PROCESS_SYL : the WORDPHON coming from RECOMB is matched against the phonBuf and (new) SYLLPHON items
122 * are created. (the original wordphon is consumed)
123 * - FEED : feeds one item and returns to spho->feedFollowState
124 * - SHIFT : items in inBuf are shifted left to make room for new items. If a sentence doesn't fit
125 * inBuf in its entirety, left and/or right contexts are kept so they can be considered in
126 * the next transduction.
127 */
128
129
130
131 /* PU sphoStep states */
132 #define SPHO_STEPSTATE_INIT 0
133 #define SPHO_STEPSTATE_COLLECT 1
134 #define SPHO_STEPSTATE_PROCESS_PARSE 2
135 #define SPHO_STEPSTATE_PROCESS_TRANSDUCE 3
136 #define SPHO_STEPSTATE_PROCESS_BOUNDS 4
137 #define SPHO_STEPSTATE_PROCESS_RECOMB 5
138 #define SPHO_STEPSTATE_PROCESS_SYL 6
139 #define SPHO_STEPSTATE_FEED 7
140 #define SPHO_STEPSTATE_SHIFT 8
141
142 #define SPHO_POS_INVALID (PICOTRNS_POS_INVALID) /* indicates that no position was set yet */
143
144 /* nr item restriction: maximum number of extended item heads in headx */
145 #define SPHO_MAXNR_HEADX 60
146
147 /* nr item restriction: maximum size of all item contents together in cont */
148 #define SPHO_MAXSIZE_CBUF (30 * 255)
149
150 /* "expanded head": item head expanded by a content position and a by boundary information
151 * potentially inserted "to the left" of the item */
152 typedef struct {
153 picodata_itemhead_t head;
154 picoos_uint16 cind;
155 picoos_uint8 boundstrength; /* bstrength to the left, 0 if not set */
156 picoos_uint8 phrasetype; /* btype for following phrase, 0 if not set */
157 picoos_int16 sildur; /* silence duration for boundary, -1 if not set */
158 } picospho_headx_t;
159
160
161
162 #define SPHO_MSGSTR_SIZE 32
163
164 /** object : SentPhoUnit
165 * shortcut : spho
166 * derived from : picodata_ProcessingUnit
167 */
168 typedef struct spho_subobj {
169 picoos_Common common;
170
171 /* we use int16 for buffer positions so we can indicate exceptional positions (invalid etc.) with negative
172 * integers */
173 picoos_uint8 procState; /* for next processing step decision */
174
175 /* buffer for item headers */
176 picoos_uint8 tmpbuf[PICODATA_MAX_ITEMSIZE]; /* tmp. location for an item */
177
178 picospho_headx_t headx[SPHO_MAXNR_HEADX]; /* "expanded head" buffer */
179 picoos_uint16 headxBufSize; /* actually allocated size (if one day headxBuf is allocated dynamically) */
180 picoos_uint16 headxReadPos, headxWritePos;
181
182 picoos_uint8 cbuf[SPHO_MAXSIZE_CBUF];
183 picoos_uint16 cbufBufSize; /* actually allocated size */
184 picoos_uint16 cbufWritePos; /* next position to write to, 0 if buffer empty */
185
186 picoos_uint8 outBuf[PICODATA_BUFSIZE_DEFAULT]; /* internal output buffer to hold just one item */
187 picoos_uint16 outBufSize; /* actually allocated size (if one day outBuf is allocated dynamically) */
188 picoos_uint16 outReadPos; /* next pos to read from inBuf for output */
189
190 /* picoos_int16 outWritePos; */ /* next pos to output from in buf */
191
192 picoos_uint8 sylBuf[255]; /* internal buffer to hold contents of syl item to be output */
193 picoos_uint8 sylReadPos, sylWritePos; /* next pos to read from sylBuf, next pos to write to sylBuf */
194
195 /* buffer for internal calculation of transducer */
196 picotrns_AltDesc altDescBuf;
197 /* the number of AltDesc in the buffer */
198 picoos_uint16 maxAltDescLen;
199
200 /* the input to a transducer should not be larger than PICOTRNS_MAX_NUM_POSSYM
201 * so the output may expand (up to 4*PICOTRNS_MAX_NUM_POSSYM) */
202
203 picotrns_possym_t phonBufA[4 * PICOTRNS_MAX_NUM_POSSYM + 1];
204 picotrns_possym_t phonBufB[4 * PICOTRNS_MAX_NUM_POSSYM + 1];
205 picotrns_possym_t * phonBuf;
206 picotrns_possym_t * phonBufOut;
207 picoos_uint16 phonReadPos, phonWritePos; /* next pos to read from phonBufIn, next pos to write to phonBufIn */
208
209 picoos_int16 activeStartPos; /* start position of items to be treated (at end of left context) */
210 picoos_int16 penultima, activeEndPos; /* positions of last two bounds/words; SPHO_POS_INVALID means uninitialized */
211 picoos_int16 lastPhraseBoundPos; /* position of the last bound encountered (<0 if inexistent or not reachable */
212 picoos_uint8 lastPhraseType; /* phrase type of the last phrase boundary, 0 if not set */
213
214 picoos_uint8 needMoreInput, /* more data necessary to decide on token */
215 suppressParseWordBound, /* dont produce word boundary */
216 suppressRecombWordBound, /* dont produce word boundary */
217 breakPending, /* received a break but didn't interpret it yet */
218 /* sentEnd, */ /* sentence end detected */
219 force, /* in forced state */
220 wordStarted, /* is it the first syl in the word: expect POS */
221 sentenceStarted;
222
223 picoos_uint16 breakTime; /* time argument of the pending break command */
224
225 picoos_uint8 feedFollowState; /* where to return after feed */
226
227 /* fst knowledge bases */
228 picoos_uint8 numFsts;
229 picokfst_FST fst[PICOKNOW_MAX_NUM_SPHO_FSTS];
230 picoos_uint8 curFst; /* the fst to be applied next */
231
232 /* fixed ids knowledge base */
233 picoktab_FixedIds fixedIds;
234
235 /* phones kb */
236 picoktab_Phones phones;
237
238 /* some soecial ids from phones */
239 picoos_uint8 primStressId, secondStressId, syllSepId;
240
241 } spho_subobj_t;
242
243
sphoReset(register picodata_ProcessingUnit this)244 static pico_status_t sphoReset(register picodata_ProcessingUnit this)
245 {
246
247 spho_subobj_t * spho;
248
249 if (NULL == this || NULL == this->subObj) {
250 return picoos_emRaiseException(this->common->em,
251 PICO_ERR_NULLPTR_ACCESS, NULL, NULL);
252 }
253 spho = (spho_subobj_t *) this->subObj;
254
255 spho->curFst = 0;
256
257 /* processing state */
258 spho->procState = SPHO_STEPSTATE_INIT;
259 spho->needMoreInput = TRUE;
260 spho->suppressParseWordBound = FALSE;
261 spho->suppressRecombWordBound = FALSE;
262 spho->breakPending = FALSE;
263 spho->force = 0;
264 spho->sentenceStarted = 0;
265
266
267 /* item buffer headx/cbuf */
268 spho->headxBufSize = SPHO_MAXNR_HEADX;
269 spho->headxReadPos = 0;
270 spho->headxWritePos = 0;
271
272 spho->cbufWritePos = 0;
273 spho->cbufBufSize = SPHO_MAXSIZE_CBUF;
274
275 /* possym buffer */
276 spho->phonBuf = spho->phonBufA;
277 spho->phonBufOut = spho->phonBufB;
278 spho->phonReadPos = 0;
279
280 /* overlapping */
281 spho->activeStartPos = 0;
282 spho->penultima = SPHO_POS_INVALID;
283 spho->activeEndPos = SPHO_POS_INVALID;
284
285 return PICO_OK;
286 }
287
288
sphoInitialize(register picodata_ProcessingUnit this,picoos_int32 r_mode)289 static pico_status_t sphoInitialize(register picodata_ProcessingUnit this, picoos_int32 r_mode)
290 {
291 picoos_uint8 i;
292 spho_subobj_t * spho;
293 picokfst_FST fst;
294
295 picoknow_kb_id_t myKbIds[PICOKNOW_MAX_NUM_SPHO_FSTS] = PICOKNOW_KBID_SPHO_ARRAY;
296
297 PICODBG_DEBUG(("init"));
298
299 if (NULL == this || NULL == this->subObj) {
300 return picoos_emRaiseException(this->common->em,
301 PICO_ERR_NULLPTR_ACCESS, NULL, NULL);
302 }
303
304 spho = (spho_subobj_t *) this->subObj;
305
306 spho->numFsts = 0;
307
308 spho->curFst = 0;
309
310 for (i = 0; i<PICOKNOW_MAX_NUM_SPHO_FSTS; i++) {
311 fst = picokfst_getFST(this->voice->kbArray[myKbIds[i]]);
312 if (NULL != fst) {
313 spho->fst[spho->numFsts++] = fst;
314 }
315 }
316 spho->fixedIds = picoktab_getFixedIds(this->voice->kbArray[PICOKNOW_KBID_FIXED_IDS]);
317 spho->phones = picoktab_getPhones(this->voice->kbArray[PICOKNOW_KBID_TAB_PHONES]);
318
319 spho->syllSepId = picoktab_getSyllboundID(spho->phones);
320 spho->primStressId = picoktab_getPrimstressID(spho->phones);
321 spho->secondStressId = picoktab_getSecstressID(spho->phones);
322
323 PICODBG_DEBUG(("got %i fsts", spho->numFsts));
324
325
326 return sphoReset(this);
327
328 }
329
330 static picodata_step_result_t sphoStep(register picodata_ProcessingUnit this,
331 picoos_int16 mode, picoos_uint16 *numBytesOutput);
332
333
334
335
sphoTerminate(register picodata_ProcessingUnit this)336 static pico_status_t sphoTerminate(register picodata_ProcessingUnit this)
337 {
338 return PICO_OK;
339 }
340
341
sphoSubObjDeallocate(register picodata_ProcessingUnit this,picoos_MemoryManager mm)342 static pico_status_t sphoSubObjDeallocate(register picodata_ProcessingUnit this,
343 picoos_MemoryManager mm)
344 {
345 spho_subobj_t * spho;
346
347 spho = (spho_subobj_t *) this->subObj;
348
349 if (NULL != this) {
350 if (NULL != this->subObj) {
351 spho = (spho_subobj_t *) (this->subObj);
352 picotrns_deallocate_alt_desc_buf(spho->common->mm,&spho->altDescBuf);
353 picoos_deallocate(mm, (void *) &this->subObj);
354 }
355 }
356 return PICO_OK;
357 }
358
picospho_newSentPhoUnit(picoos_MemoryManager mm,picoos_Common common,picodata_CharBuffer cbIn,picodata_CharBuffer cbOut,picorsrc_Voice voice)359 picodata_ProcessingUnit picospho_newSentPhoUnit(picoos_MemoryManager mm,
360 picoos_Common common, picodata_CharBuffer cbIn,
361 picodata_CharBuffer cbOut, picorsrc_Voice voice)
362 {
363 spho_subobj_t * spho;
364
365 picodata_ProcessingUnit this = picodata_newProcessingUnit(mm, common, cbIn, cbOut, voice);
366 if (this == NULL) {
367 return NULL;
368 }
369
370 this->initialize = sphoInitialize;
371 this->step = sphoStep;
372 this->terminate = sphoTerminate;
373 this->subDeallocate = sphoSubObjDeallocate;
374
375 this->subObj = picoos_allocate(mm, sizeof(spho_subobj_t));
376 if (this->subObj == NULL) {
377 picoos_deallocate(mm, (void **)(void*)&this);
378 return NULL;
379 }
380 spho = (spho_subobj_t *) this->subObj;
381
382 spho->common = this->common;
383
384 /* these are given by the pre-allocated array sizes */
385 spho->outBufSize = PICODATA_BUFSIZE_DEFAULT;
386
387
388 spho->altDescBuf = picotrns_allocate_alt_desc_buf(spho->common->mm, SPHO_MAX_ALTDESC_SIZE, &spho->maxAltDescLen);
389 if (NULL == spho->altDescBuf) {
390 picotrns_deallocate_alt_desc_buf(spho->common->mm,&spho->altDescBuf);
391 picoos_emRaiseException(spho->common->em,PICO_EXC_OUT_OF_MEM, NULL,NULL);
392 return NULL;
393 }
394
395 sphoInitialize(this, PICO_RESET_FULL);
396 return this;
397 }
398
399
400 /* ***********************************************************************/
401 /* process buffered item list */
402 /* ***********************************************************************/
403
404
405 /* shift relevant data in headx/'cbuf' (between 'readPos' incl and writePos non-incl) to 'start'.
406 * modify read/writePos accordingly */
shift_range_left_1(spho_subobj_t * spho,picoos_int16 * from,picoos_int16 to)407 static picoos_int16 shift_range_left_1(spho_subobj_t *spho, picoos_int16 * from, picoos_int16 to)
408 {
409
410 /* remember shift parameters for cbuf */
411 picoos_uint16
412 c_i,
413 c_j,
414 c_diff,
415 c_writePos,
416 i,
417 j,
418 diff,
419 writePos;
420 i = to;
421 j = *from;
422 diff = j-i;
423 writePos = spho->headxWritePos;
424 c_i = spho->headx[to].cind;
425 if (j < writePos) {
426 c_j = spho->headx[j].cind;
427 } else {
428 c_j = spho->cbufWritePos;
429 }
430 c_diff = c_j - c_i;
431 c_writePos = spho->cbufWritePos;
432
433 PICODBG_DEBUG((
434 "shifting buffer region [%i,%i[ down to %i",*from, writePos, to
435 ));
436
437
438 /* PICODBG_ASSERT((i<j)); */
439 if (i > j) {
440 return -1;
441 }
442 /* shift cbuf */
443 while (c_j < c_writePos) {
444 spho->cbuf[c_i++] = spho->cbuf[c_j++];
445 }
446 /* shift headx */
447 while (j < writePos) {
448 spho->headx[j].cind -= c_diff;
449 spho->headx[i++] = spho->headx[j++];
450 }
451 spho->headxWritePos -= diff;
452 *from = to;
453 spho->cbufWritePos -= c_diff;
454 /* */
455 PICODBG_DEBUG((
456 "readPos,WritePos are now [%i,%i[, returning shift amount %i",*from, spho->headxWritePos, diff
457 ));
458 return diff;
459 }
460
sphoAddPhoneme(register spho_subobj_t * spho,picoos_int16 pos,picoos_int16 sym)461 static pico_status_t sphoAddPhoneme(register spho_subobj_t *spho, picoos_int16 pos, picoos_int16 sym) {
462 picoos_uint8 plane, unshifted;
463 /* just for debuging */
464 unshifted = picotrns_unplane(sym,&plane);
465 PICODBG_TRACE(("adding %i/%i (%c on plane %i) at phonBuf[%i]",pos,sym,unshifted,plane,spho->phonWritePos));
466 if (2* PICOTRNS_MAX_NUM_POSSYM <= spho->phonWritePos) {
467 /* not an error! */
468 PICODBG_DEBUG(("couldn't add because phon buffer full"));
469 return PICO_EXC_BUF_OVERFLOW;
470 } else {
471 spho->phonBuf[spho->phonWritePos].pos = pos;
472 spho->phonBuf[spho->phonWritePos].sym = sym;
473 spho->phonWritePos++;
474 return PICO_OK;
475 }
476 }
477
sphoAddStartPhoneme(register spho_subobj_t * spho)478 static pico_status_t sphoAddStartPhoneme(register spho_subobj_t *spho) {
479 return sphoAddPhoneme(spho, PICOTRNS_POS_IGNORE,
480 (PICOKFST_PLANE_INTERN << 8) + spho->fixedIds->phonStartId);
481 }
482
sphoAddTermPhonemes(register spho_subobj_t * spho,picoos_uint16 pos)483 static pico_status_t sphoAddTermPhonemes(register spho_subobj_t *spho, picoos_uint16 pos) {
484 return sphoAddPhoneme(spho, pos,
485 (PICOKFST_PLANE_PB_STRENGTHS << 8) + PICODATA_ITEMINFO1_BOUND_SEND)
486 && sphoAddPhoneme(spho, PICOTRNS_POS_IGNORE,
487 (PICOKFST_PLANE_INTERN << 8) + spho->fixedIds->phonTermId);
488 }
489
490 /* return "syllable accent" (or prominence) symbol, given "word accent" symbol 'wacc' and stress value (no=0, primary=1, secondary=2) */
sphoGetSylAccent(register spho_subobj_t * spho,picoos_uint8 wacc,picoos_uint8 sylStress)491 static picoos_uint16 sphoGetSylAccent(register spho_subobj_t *spho,
492 picoos_uint8 wacc, picoos_uint8 sylStress)
493 {
494 PICODBG_ASSERT(sylStress <= 2);
495
496 spho = spho; /* avoid warning "var not used in this function"*/
497
498 switch (sylStress) {
499 case 0: /* non-stressed syllable gets no prominence */
500 /* return spho->fixedIds->accId[0]; */
501 return PICODATA_ACC0;
502 break;
503 case 1: /* primary-stressed syllable gets word prominence */
504 return wacc;
505 break;
506 case 2: /* secondary-stressed syllable gets no prominence or secondary stress prom. (4) */
507 return (PICODATA_ACC0 == wacc) ? PICODATA_ACC0
508 : PICODATA_ACC4;
509 /*return (spho->fixedIds->accId[0] == wacc) ? spho->fixedIds->accId[0]
510 : spho->fixedIds->accId[4]; */
511 break;
512 default:
513 /* never occurs :-) */
514 return PICODATA_ACC0;
515 break;
516 }
517 }
518
519
520 /* ***********************************************************************/
521 /* extract phonemes of an item into a phonBuf */
522 /* ***********************************************************************/
sphoExtractPhonemes(register picodata_ProcessingUnit this,register spho_subobj_t * spho,picoos_uint16 pos,picoos_uint8 convertAccents,picoos_uint8 * suppressWB)523 static pico_status_t sphoExtractPhonemes(register picodata_ProcessingUnit this,
524 register spho_subobj_t *spho, picoos_uint16 pos,
525 picoos_uint8 convertAccents, picoos_uint8 * suppressWB)
526 {
527 pico_status_t rv = PICO_OK;
528 picoos_uint16 i, j;
529 picoos_int16 fstSymbol;
530 picoos_uint8 curStress;
531 picotrns_possym_t tmpPosSym;
532 picoos_uint16 oldPos, curPos;
533 picodata_itemhead_t * head;
534 picoos_uint8* content;
535
536 #if defined(PICO_DEBUG)
537 picoos_char msgstr[SPHO_MSGSTR_SIZE];
538 #endif
539
540
541 /*
542 Items considered in a transduction are a BOUND or a WORDPHON item. its starting offset within the
543 headxBuf is given as 'pos'.
544 Elements that go into the transduction receive "their" position in the buffer.
545 */
546
547 oldPos = spho->phonWritePos;
548
549 head = &(spho->headx[pos].head);
550 content = spho->cbuf + spho->headx[pos].cind;
551
552 PICODBG_TRACE(("doing item %s\n",
553 picodata_head_to_string(head,msgstr,SPHO_MSGSTR_SIZE)));
554
555 switch (head->type) {
556 case PICODATA_ITEM_BOUND:
557 /* map SBEG, SEND and TERM (as sentence closing) to SEND */
558 fstSymbol = (PICODATA_ITEMINFO1_BOUND_SBEG == head->info1 || PICODATA_ITEMINFO1_BOUND_TERM == head->info1) ? PICODATA_ITEMINFO1_BOUND_SEND : head->info1;
559 PICODBG_TRACE(("found bound of type %c\n",head->info1));
560 /* BOUND(<bound strength><phrase type>) */
561 /* insert bound strength */
562 PICODBG_TRACE(("inserting phrase bound phoneme %c and setting suppresWB=1\n",fstSymbol));
563 fstSymbol += (PICOKFST_PLANE_PB_STRENGTHS << 8);
564 rv = sphoAddPhoneme(spho,pos,fstSymbol);
565 /* phrase type not used */
566 /* suppress next word boundary */
567 (*suppressWB) = 1;
568 break;
569
570 case PICODATA_ITEM_WORDPHON:
571 /* WORDPHON(POS,WACC)phon */
572 PICODBG_TRACE(("found WORDPHON"));
573 /* insert word boundary if not suppressed */
574 if (!(*suppressWB)) {
575 fstSymbol = (PICOKFST_PLANE_PB_STRENGTHS << 8) + PICODATA_ITEMINFO1_BOUND_PHR0;
576 PICODBG_TRACE(("adding word boundary phone"));
577 rv = sphoAddPhoneme(spho,pos,fstSymbol);
578 }
579 (*suppressWB) = 0;
580 /* for the time being, we force to use POS so we can transduce all fsts in a row without reconsulting the items */
581
582
583 /* If 'convertAccents' then the accentuation is not directly encoded. It rather influences the mapping of
584 * the word accent symbol to the actual accent phoneme which is put after the syllable separator. */
585 if (convertAccents) {
586 PICODBG_TRACE(("converting accents"));
587 /* extracting phonemes IN REVERSE order replacing syllable symbols with prominence symbols */
588 curPos = spho->phonWritePos;
589 curStress = 0; /* no stress */
590 for (i = head->len; i > 0 ;) {
591 i--;
592 if (spho->primStressId == content[i]) {
593 curStress = 1;
594 PICODBG_DEBUG(("skipping primary stress at pos %i (in 1 .. %i)",i, head->len));
595 continue; /* skip primary stress symbol */
596 } else if (spho->secondStressId == content[i]) {
597 curStress = 2;
598 PICODBG_DEBUG(("skipping secondary stress at pos %i (in 1 .. %i)",i, head->len));
599 continue; /* skip secundary stress symbol */
600 } else if (spho->syllSepId == content[i]) {
601 fstSymbol = (PICOKFST_PLANE_POS << 8) + head->info1;
602 rv = sphoAddPhoneme(spho, pos, fstSymbol);
603 /* replace syllSepId by combination of syllable stress and word prominence */
604 fstSymbol = sphoGetSylAccent(spho,head->info2,curStress);
605 curStress = 0;
606 /* add accent */
607 fstSymbol += (PICOKFST_PLANE_ACCENTS << 8);
608 rv = sphoAddPhoneme(spho,pos,fstSymbol);
609 if (PICO_OK != rv) {
610 break;
611 }
612 /* and keep syllable boundary */
613 fstSymbol = (PICOKFST_PLANE_PHONEMES << 8) + content[i];
614 } else {
615 /* normal phoneme */
616 fstSymbol = (PICOKFST_PLANE_PHONEMES << 8) + content[i];
617 }
618 if (PICO_OK == rv) {
619 rv = sphoAddPhoneme(spho,pos,fstSymbol);
620 }
621 }
622 if (PICO_OK == rv) {
623 /* bug 366: we position the "head" into the item header and not on the first phoneme
624 * because there might be no phonemes at all */
625 /* insert head of the first syllable of a word */
626 fstSymbol = (PICOKFST_PLANE_POS << 8) + head->info1;
627 rv = sphoAddPhoneme(spho,pos,fstSymbol);
628 fstSymbol = sphoGetSylAccent(spho,head->info2,curStress);
629 curStress = 0;
630 fstSymbol += (PICOKFST_PLANE_ACCENTS << 8);
631 rv = sphoAddPhoneme(spho,pos,fstSymbol);
632 }
633 if (PICO_OK == rv) {
634 /* invert sympos portion */
635 i = curPos;
636 j=spho->phonWritePos-1;
637 while (i < j) {
638 tmpPosSym.pos = spho->phonBuf[i].pos;
639 tmpPosSym.sym = spho->phonBuf[i].sym;
640 spho->phonBuf[i].pos = spho->phonBuf[j].pos;
641 spho->phonBuf[i].sym = spho->phonBuf[j].sym;
642 spho->phonBuf[j].pos = tmpPosSym.pos;
643 spho->phonBuf[j].sym = tmpPosSym.sym;
644 i++;
645 j--;
646 }
647 }
648 } else { /* convertAccents */
649 for (i = 0; i <head->len; i++) {
650 fstSymbol = (PICOKFST_PLANE_PHONEMES << 8) + content[i];
651 rv = sphoAddPhoneme(spho,pos,fstSymbol);
652 }
653 }
654 break;
655 default:
656 picoos_emRaiseException(this->common->em,rv,NULL,NULL);
657 break;
658 } /* switch(head->type) */
659 if (PICO_OK != rv) {
660 spho->phonWritePos = oldPos;
661 }
662 return rv;
663 }
664
665
666
667
668
669 #define SPHO_POSSYM_OK 0
670 #define SPHO_POSSYM_OUT_OF_RANGE 1
671 #define SPHO_POSSYM_END 2
672 #define SPHO_POSSYM_INVALID -3
673 /* *readPos is the next position in phonBuf to be read, and *writePos is the first position not to be read (may be outside
674 * buf).
675 * 'rangeEnd' is the first possym position outside the desired range.
676 * Possible return values:
677 * SPHO_POSSYM_OK : 'pos' and 'sym' are set to the read possym, *readPos is advanced
678 * SPHO_POSSYM_OUT_OF_RANGE : pos is out of range. 'pos' is set to that of the read possym, 'sym' is undefined
679 * SPHO_POSSYM_UNDERFLOW : no more data in buf. 'pos' is set to PICOTRNS_POS_INVALID, 'sym' is undefined
680 * SPHO_POSSYM_INVALID : "strange" pos. 'pos' is set to PICOTRNS_POS_INVALID, 'sym' is undefined
681 */
getNextPosSym(spho_subobj_t * spho,picoos_int16 * pos,picoos_int16 * sym,picoos_int16 rangeEnd)682 static pico_status_t getNextPosSym(spho_subobj_t * spho, picoos_int16 * pos, picoos_int16 * sym,
683 picoos_int16 rangeEnd) {
684 /* skip POS_IGNORE */
685 while ((spho->phonReadPos < spho->phonWritePos) && (PICOTRNS_POS_IGNORE == spho->phonBuf[spho->phonReadPos].pos)) {
686 PICODBG_DEBUG(("ignoring phone at spho->phonBuf[%i] because it has pos==IGNORE",spho->phonReadPos));
687 spho->phonReadPos++;
688 }
689 if ((spho->phonReadPos < spho->phonWritePos)) {
690 *pos = spho->phonBuf[spho->phonReadPos].pos;
691 if ((PICOTRNS_POS_INSERT == *pos) || ((0 <= *pos) && (*pos < rangeEnd))) {
692 *sym = spho->phonBuf[spho->phonReadPos++].sym;
693 return SPHO_POSSYM_OK;
694 } else if (*pos < 0){ /* *pos is "strange" (e.g. POS_INVALID) */
695 return SPHO_POSSYM_INVALID;
696 } else {
697 return SPHO_POSSYM_OUT_OF_RANGE;
698 }
699 } else {
700 /* no more possyms to read */
701 *pos = PICOTRNS_POS_INVALID;
702 return SPHO_POSSYM_END;
703 }
704 }
705
706
707
708 /** Calculate bound strength modified by transduction
709 *
710 * Given the original bound strength 'orig' and the desired target strength 'target' (suggested by fst),
711 * calculate the modified bound strength.
712 *
713 * @param orig original bound strength
714 * @param target target bound strength
715 * @return resulting bound strength
716 */
fstModifiedBoundStrength(picoos_uint8 orig,picoos_uint8 target)717 static picoos_uint8 fstModifiedBoundStrength(picoos_uint8 orig, picoos_uint8 target)
718 {
719 switch (orig) {
720 case PICODATA_ITEMINFO1_BOUND_PHR1:
721 case PICODATA_ITEMINFO1_BOUND_PHR2:
722 /* don't allow primary phrase bounds to be demoted to word bound */
723 if (PICODATA_ITEMINFO1_BOUND_PHR0 == target) {
724 return PICODATA_ITEMINFO1_BOUND_PHR3;
725 }
726 case PICODATA_ITEMINFO1_BOUND_PHR0:
727 case PICODATA_ITEMINFO1_BOUND_PHR3:
728 return target;
729 break;
730 default:
731 /* don't allow bounds other than phrase or word bounds to be changed */
732 return orig;
733 break;
734 }
735 }
736
737 /** Calculate bound strength modified by a \<break> command
738 *
739 * Given the original (predicted and possibly fst-modified) bound strength, and a time value from an
740 * overwriding \<break> command, calculate the modified bound strength.
741 *
742 * @param orig original bound strength
743 * @param time time given as property of \<break> command
744 * @param wasPrimary
745 * @return modified bound strength
746 */
breakModifiedBoundStrength(picoos_uint8 orig,picoos_uint16 time,picoos_bool wasPrimary)747 static picoos_uint8 breakModifiedBoundStrength(picoos_uint8 orig, picoos_uint16 time, picoos_bool wasPrimary)
748 {
749 picoos_uint8 modified = (0 == time) ? PICODATA_ITEMINFO1_BOUND_PHR3 :
750 (50 < time) ? PICODATA_ITEMINFO1_BOUND_PHR1 : PICODATA_ITEMINFO1_BOUND_PHR2;
751 switch (orig) {
752 /* for word and phrase breaks, return 'modified', unless a non-silence gets time==0, in which
753 * case return no break (word break) */
754 case PICODATA_ITEMINFO1_BOUND_PHR0:
755 if (0 == time) {
756 return PICODATA_ITEMINFO1_BOUND_PHR0;
757 }
758 case PICODATA_ITEMINFO1_BOUND_PHR3:
759 if (!wasPrimary && (0 == time)) {
760 return PICODATA_ITEMINFO1_BOUND_PHR0;
761 }
762 case PICODATA_ITEMINFO1_BOUND_PHR1:
763 case PICODATA_ITEMINFO1_BOUND_PHR2:
764 return modified;
765 break;
766 default:
767 return orig;
768 break;
769 }
770 }
771
breakStateInterrupting(picodata_itemhead_t * head,picoos_bool * breakBefore,picoos_bool * breakAfter)772 static picoos_bool breakStateInterrupting(picodata_itemhead_t * head,
773 picoos_bool * breakBefore, picoos_bool * breakAfter) {
774
775 picoos_bool result = 1;
776
777 *breakBefore = 0;
778 *breakAfter = 0;
779
780 if (PICODATA_ITEM_WORDPHON == head->type) {
781
782 } else if (PICODATA_ITEM_CMD == head->type) {
783 if ((PICODATA_ITEMINFO1_CMD_PLAY == head->info1)
784 || (PICODATA_ITEMINFO1_CMD_SAVE == head->info1)
785 || (PICODATA_ITEMINFO1_CMD_UNSAVE == head->info1)) {
786 *breakBefore = 1;
787 *breakAfter = 1;
788 } else if (PICODATA_ITEMINFO1_CMD_SAVE == head->info1) {
789 *breakBefore = 1;
790 } else if (PICODATA_ITEMINFO1_CMD_UNSAVE == head->info1) {
791 *breakAfter = 1;
792 } else if (PICODATA_ITEMINFO1_CMD_IGNSIG == head->info1) {
793 if (PICODATA_ITEMINFO2_CMD_START == head->info2) {
794 *breakBefore = 1;
795 } else {
796 *breakAfter = 1;
797 }
798 }
799 } else {
800 result = 0;
801 }
802 return result;
803 }
804
805
putSideBoundToOutput(spho_subobj_t * spho)806 static void putSideBoundToOutput(spho_subobj_t * spho)
807 {
808
809 picodata_itemhead_t ohead;
810 picoos_uint8 ocontent[2*sizeof(picoos_uint16)];
811 picoos_int16 sildur;
812 picoos_uint16 clen;
813
814 /* create boundary */
815 ohead.type = PICODATA_ITEM_BOUND;
816 ohead.info1 = spho->headx[spho->outReadPos].boundstrength;
817 ohead.info2 = spho->headx[spho->outReadPos].phrasetype;
818 sildur = spho->headx[spho->outReadPos].sildur;
819 if ((sildur < 0)
820 || (PICODATA_ITEMINFO1_BOUND_PHR0 == ohead.info1)
821 || (PICODATA_ITEMINFO1_BOUND_PHR3 == ohead.info1)) {
822 PICODBG_DEBUG(("outputting a bound of strength '%c' and type '%c' without duration constraints",ohead.info1, ohead.info2));
823 ohead.len = 0;
824 } else {
825 picoos_uint32 pos = 0;
826 picoos_write_mem_pi_uint16(ocontent,&pos,sildur);
827 picoos_write_mem_pi_uint16(ocontent,&pos,sildur);
828 PICODBG_DEBUG(("outputting a bound of strength '%c' and type '%c' with duration constraints [%i,%i]",ohead.info1, ohead.info2,sildur, sildur));
829 ohead.len = pos;
830 }
831 picodata_put_itemparts(&ohead, ocontent, ohead.len,
832 spho->outBuf, spho->outBufSize, &clen);
833 /* disable side bound */
834 spho->headx[spho->outReadPos].boundstrength = 0;
835 }
836
837 /** Set bound strength and sil dur.
838 *
839 * given the original bound strength 'orig_strength' and the fst-suggested bound strength 'fst_strength'
840 * and possibly being in a pending break state, calculate the resulting bound strength and set boundstrength
841 * and sildur of the current item (spho->headx[spho->outReadPos]) accordingly.
842 * if a boundstrength was set, also calculate the phrasetype and if necessary (and reachable), modify the phrase type
843 * of the previous phrase boundary.
844 *
845 * @param spho
846 * @param orig_strength
847 * @param orig_type
848 * @param fst_strength
849 */
setSideBound(spho_subobj_t * spho,picoos_uint8 orig_strength,picoos_uint8 orig_type,picoos_uint8 fst_strength)850 static void setSideBound(spho_subobj_t * spho, picoos_uint8 orig_strength, picoos_uint8 orig_type, picoos_uint8 fst_strength) {
851 picoos_uint8 strength;
852
853 /* insert modified bound according to transduction symbol, if any */
854 if (PICODATA_ITEMINFO1_NA == orig_strength) {
855 /* no original/fst strength given */
856 orig_strength = PICODATA_ITEMINFO1_BOUND_PHR0;
857 strength = PICODATA_ITEMINFO1_BOUND_PHR0;
858 } else {
859 strength = fstModifiedBoundStrength(orig_strength,fst_strength);
860 spho->headx[spho->outReadPos].boundstrength = strength;
861 spho->headx[spho->outReadPos].sildur = -1;
862 PICODBG_DEBUG(("setting bound strength to fst-suggested value %c (was %c)",strength, spho->headx[spho->outReadPos].boundstrength, spho->breakTime));
863 }
864
865 /* insert modified bound according to pending break, if any */
866 if (spho->breakPending) {
867 /* the calculation is based on the fst-modified value (because this is what the customer wants to
868 * override)
869 */
870 strength = breakModifiedBoundStrength(strength, spho->breakTime, (PICODATA_ITEMINFO1_BOUND_PHR1 == orig_strength));
871 PICODBG_DEBUG(("setting bound strength to break-imposed value %c (was %c) and time to %i",strength, spho->headx[spho->outReadPos].boundstrength, spho->breakTime));
872 spho->headx[spho->outReadPos].boundstrength = strength;
873 spho->headx[spho->outReadPos].sildur = spho->breakTime;
874 spho->breakPending = FALSE;
875 }
876 if (spho->headx[spho->outReadPos].boundstrength) {
877 /* we did set a bound strength, possibly promoting or demoting a boundary; now set the phrase type
878 * possibly also changing the phrase type of the previous phrase bound
879 */
880 picoos_uint8 fromPhrase = ((PICODATA_ITEMINFO1_BOUND_PHR0 != orig_strength));
881 picoos_uint8 toPhrase = ((PICODATA_ITEMINFO1_BOUND_PHR0 != strength));
882
883 PICODBG_DEBUG(("setting phrase type (wasPhrase=%i, isPhrase=%i)",fromPhrase,toPhrase));
884 if (toPhrase) {
885 if (fromPhrase) {
886 spho->lastPhraseType = orig_type;
887 } else { /*promote */
888 if (spho->activeStartPos <= spho->lastPhraseBoundPos) {
889 /* we still can change prev phrase bound */
890 /* since a new phrase boundary is introduced, we have to 'invent'
891 * an additional phrase type here. For that, we have to use some of the
892 * knowledge that otherwise is handled in picoacph.
893 */
894 spho->headx[spho->lastPhraseBoundPos].phrasetype
895 = PICODATA_ITEMINFO2_BOUNDTYPE_P;
896 }
897 }
898 spho->lastPhraseBoundPos = spho->outReadPos;
899 spho->headx[spho->lastPhraseBoundPos].phrasetype
900 = spho->lastPhraseType;
901
902 } else {
903 spho->headx[spho->outReadPos].phrasetype = PICODATA_ITEMINFO2_NA;
904 if (fromPhrase) { /* demote */
905 spho->lastPhraseType = orig_type;
906 if (spho->activeStartPos <= spho->lastPhraseBoundPos) {
907 /* we still can change prev phrase bound */
908 spho->headx[spho->lastPhraseBoundPos].phrasetype
909 = spho->lastPhraseType;
910 }
911 }
912 }
913 }
914 }
915
916
917 /* ***********************************************************************/
918 /* sphoStep function */
919 /* ***********************************************************************/
920
921
sphoStep(register picodata_ProcessingUnit this,picoos_int16 mode,picoos_uint16 * numBytesOutput)922 static picodata_step_result_t sphoStep(register picodata_ProcessingUnit this,
923 picoos_int16 mode, picoos_uint16 * numBytesOutput)
924 {
925
926 register spho_subobj_t *spho;
927 pico_status_t rv= PICO_OK;
928 picoos_uint16 blen;
929 picodata_itemhead_t ihead, ohead;
930 picoos_uint8 *icontent;
931 picoos_uint16 nextInPos;
932 #if defined(PICO_DEBUG)
933 picoos_char msgstr[SPHO_MSGSTR_SIZE];
934 #endif
935
936 /* used in FEED and FEED_SYM */
937 picoos_uint16 clen;
938 picoos_int16 pos, sym, sylsym;
939 picoos_uint8 plane;
940
941 /* used in BOUNDS */
942 picoos_bool breakBefore, breakAfter;
943
944 /* pico_status_t rvP= PICO_OK; */
945
946 picoos_uint16 curPos /*, nextPos */;
947 picoos_uint16 remHeadxSize, remCbufSize;
948
949
950 if (NULL == this || NULL == this->subObj) {
951 return PICODATA_PU_ERROR;
952 }
953 spho = (spho_subobj_t *) this->subObj;
954
955 mode = mode; /* avoid warning "var not used in this function"*/
956
957 *numBytesOutput = 0;
958 while (1) { /* exit via return */
959 PICODBG_INFO(("doing state %i, headxReadPos: %d, headxWritePos: %d",
960 spho->procState, spho->headxReadPos, spho->headxWritePos));
961
962 switch (spho->procState) {
963
964 case SPHO_STEPSTATE_INIT:
965 /* **********************************************************************/
966 /* INIT */
967 /* **********************************************************************/
968 PICODBG_DEBUG(("INIT"));
969 /* (re)set values for PARSE */
970 spho->penultima = SPHO_POS_INVALID;
971 spho->activeEndPos = SPHO_POS_INVALID;
972 spho->headxReadPos = 0;
973 spho->phonReadPos = 0;
974 spho->phonWritePos = 0;
975 spho->lastPhraseType = PICODATA_ITEMINFO2_NA;
976 spho->lastPhraseBoundPos = -1;
977
978 spho->procState = SPHO_STEPSTATE_COLLECT;
979 break;
980
981
982 case SPHO_STEPSTATE_COLLECT:
983 /* **********************************************************************/
984 /* COLLECT */
985 /* **********************************************************************/
986 /* collect state: get items from charBuf and store in
987 * internal inBuf
988 */
989 PICODBG_TRACE(("COLLECT"));
990 rv = PICO_OK;
991 remHeadxSize = spho->headxBufSize - spho->headxWritePos;
992 remCbufSize = spho->cbufBufSize - spho->cbufWritePos;
993 curPos = spho->headxWritePos;
994 while ((PICO_OK == rv) && (remHeadxSize > 0) && (remCbufSize > 0)) {
995 PICODBG_DEBUG(("COLLECT getting item at headxWritePos %i (remaining %i)",spho->headxWritePos, remHeadxSize));
996 rv = picodata_cbGetItem(this->cbIn, spho->tmpbuf, PICODATA_MAX_ITEMSIZE, &blen);
997 if (PICO_OK == rv) {
998 rv = picodata_get_itemparts(spho->tmpbuf,
999 PICODATA_MAX_ITEMSIZE, &(spho->headx[spho->headxWritePos].head),
1000 &(spho->cbuf[spho->cbufWritePos]), remCbufSize, &blen);
1001 if (PICO_OK == rv) {
1002 spho->headx[spho->headxWritePos].cind = spho->cbufWritePos;
1003 spho->headx[spho->headxWritePos].boundstrength = 0;
1004 spho->headxWritePos++;
1005 remHeadxSize--;
1006 spho->cbufWritePos += blen;
1007 remCbufSize -= blen;
1008 }
1009 }
1010 }
1011 if ((PICO_OK == rv) && ((remHeadxSize <= 0) || (remCbufSize <= 0))) {
1012 rv = PICO_EXC_BUF_OVERFLOW;
1013 }
1014
1015 /* in normal circumstances, rv is either PICO_EOF (no more items in cbIn) or PICO_BUF_OVERFLOW
1016 * (if no more items fit into headx) */
1017 if ((PICO_EOF != rv) && (PICO_EXC_BUF_OVERFLOW != rv)) {
1018 PICODBG_DEBUG(("COLLECT ** problem getting item, unhandled, rv: %i", rv));
1019 picoos_emRaiseException(this->common->em, rv,
1020 NULL, NULL);
1021 return PICODATA_PU_ERROR;
1022 }
1023 if (PICO_EOF == rv) { /* there are no more items available */
1024 if (curPos < spho->headxWritePos) { /* we did get some new items */
1025 PICODBG_DEBUG(("COLLECT read %i items",
1026 spho->headxWritePos - curPos));
1027 spho->needMoreInput = FALSE;
1028 }
1029 if (spho->needMoreInput) { /* not enough items to proceed */
1030 PICODBG_DEBUG(("COLLECT need more data, returning IDLE"));
1031 return PICODATA_PU_IDLE;
1032 } else {
1033 spho->procState = SPHO_STEPSTATE_PROCESS_PARSE;
1034 /* uncomment next to split into two steps */
1035 /* return PICODATA_PU_ATOMIC; */
1036 }
1037 } else { /* input buffer full */
1038 PICODBG_DEBUG(("COLLECT input buffer full"));
1039 if (spho->needMoreInput) { /* forced output because we can't get more data */
1040 spho->needMoreInput = FALSE;
1041 spho->force = TRUE;
1042 }
1043 spho->procState = SPHO_STEPSTATE_PROCESS_PARSE;
1044 }
1045 break;
1046
1047 case SPHO_STEPSTATE_PROCESS_PARSE:
1048
1049 /* **********************************************************************/
1050 /* PARSE: items -> input pos/phon pairs */
1051 /* **********************************************************************/
1052
1053 /* parse one item at a time */
1054 /* If
1055 * - the item is a sentence end or
1056 * - it is the last item and force=1 or
1057 * - the phon buffer is full
1058 * then set inReadPos to 0 and go to TRANSDUCE
1059 * else advance by one item */
1060
1061 /* look at the current item */
1062 PICODBG_TRACE(("PARSE"));
1063 if (spho->headxReadPos >= spho->headxWritePos) {
1064 /* no more items in headx */
1065 if (spho->force) {
1066 PICODBG_INFO(("no more items in headx but we are forced to transduce"));
1067
1068 /* headx is full; we are forced to transduce before reaching the sentence end */
1069 spho->force = FALSE;
1070 if (SPHO_POS_INVALID == spho->activeEndPos) {
1071 spho->activeEndPos = spho->headxReadPos;
1072 }
1073 spho->procState = SPHO_STEPSTATE_PROCESS_TRANSDUCE;
1074 } else {
1075 /* we try to get more data */
1076 PICODBG_INFO(("no more items in headx, try to collect more"));
1077 spho->needMoreInput = TRUE;
1078 spho->procState = SPHO_STEPSTATE_COLLECT;
1079 }
1080 break;
1081 }
1082
1083 ihead = spho->headx[spho->headxReadPos].head;
1084 icontent = spho->cbuf + spho->headx[spho->headxReadPos].cind;
1085
1086 PICODBG_DEBUG(("PARSE looking at item %s",picodata_head_to_string(&ihead,msgstr,SPHO_MSGSTR_SIZE)));
1087 /* treat header */
1088 if (PICODATA_ITEM_BOUND == ihead.type) {
1089 /* see if it is a sentence end or termination boundary (flush) */
1090 if ((PICODATA_ITEMINFO1_BOUND_SEND == ihead.info1)
1091 || (PICODATA_ITEMINFO1_BOUND_TERM == ihead.info1)) {
1092 PICODBG_INFO(("PARSE found sentence end or term BOUND"));
1093
1094 if (spho->sentenceStarted) {
1095 /* its the end of the sentence */
1096 PICODBG_INFO(("PARSE found sentence end"));
1097 spho->sentenceStarted = 0;
1098 /* there is no need for a right context; move the active end to the end */
1099 /* add sentence termination phonemes */
1100 sphoAddTermPhonemes(spho, spho->headxReadPos);
1101 spho->headxReadPos++;
1102 spho->activeEndPos = spho->headxReadPos;
1103 /* we may discard all information up to activeEndPos, after processing of last
1104 * sentence part
1105 */
1106 spho->penultima = spho->activeEndPos;
1107
1108 /* transduce */
1109 spho->procState = SPHO_STEPSTATE_PROCESS_TRANSDUCE;
1110 /* uncomment to split */
1111 /* return PICODATA_PU_BUSY; */
1112 break;
1113 } else {
1114 if (PICODATA_ITEMINFO1_BOUND_TERM == ihead.info1) {
1115 /* its the end of input (flush) */
1116 PICODBG_INFO(("PARSE forwarding input end (flush)"));
1117 /* copy item unmodified */
1118 picodata_put_itemparts(&ihead,
1119 icontent,
1120 ihead.len,
1121 spho->outBuf, spho->outBufSize,
1122 &clen);
1123
1124 spho->headxReadPos++;
1125 spho->activeEndPos = spho->headxReadPos;
1126 spho->penultima = SPHO_POS_INVALID;
1127 spho->feedFollowState = SPHO_STEPSTATE_SHIFT;
1128 spho->procState = SPHO_STEPSTATE_FEED;
1129 break;
1130 } else {
1131 /* this should never happen */
1132 /* eliminate bound */
1133 spho->headxReadPos++;
1134 spho->activeEndPos = spho->headxReadPos;
1135 spho->penultima = SPHO_POS_INVALID;
1136 PICODBG_ERROR(("PARSE found a sentence end without a sentence start; eliminated"));
1137 }
1138 }
1139 } else if (PICODATA_ITEMINFO1_BOUND_SBEG == ihead.info1) {
1140 /* its the start of the sentence */
1141 PICODBG_INFO(("PARSE found sentence start"));
1142 /* add sentence starting phoneme */
1143 sphoAddStartPhoneme(spho);
1144
1145 spho->sentenceStarted = 1;
1146 }
1147 }
1148
1149 if ((PICODATA_ITEM_WORDPHON == ihead.type)
1150 || (PICODATA_ITEM_BOUND == ihead.type)) {
1151 /* if it is a word or a bound try to extract phonemes */
1152 PICODBG_INFO(("PARSE found WORD phon or phrase BOUND"));
1153 rv = sphoExtractPhonemes(this, spho, spho->headxReadPos,
1154 TRUE /* convertAccents */,
1155 &spho->suppressParseWordBound);
1156 if (PICO_OK == rv) {
1157 PICODBG_INFO(("PARSE successfully returned from phoneme extraction"));
1158 /* replace activeEndPos if the new item is a word, or activeEndPos was not set yet, or
1159 * activeEndPos was a bound */
1160 if ((spho->activeStartPos <= spho->headxReadPos) && ((PICODATA_ITEM_WORDPHON == ihead.type)
1161 || (SPHO_POS_INVALID == spho->activeEndPos)
1162 || (PICODATA_ITEM_BOUND == spho->headx[spho->activeEndPos].head.type))) {
1163 PICODBG_INFO(("PARSE found new activeEndPos: %i,%i -> %i,%i",
1164 spho->penultima,spho->activeEndPos,spho->activeEndPos,spho->headxReadPos));
1165 spho->penultima = spho->activeEndPos;
1166 spho->activeEndPos = spho->headxReadPos;
1167 }
1168
1169 } else if (PICO_EXC_BUF_OVERFLOW == rv) {
1170 /* phoneme buffer cannot take this item anymore;
1171 if the phoneme buffer has some contents, we are forced to transduce before reaching the sentence end
1172 else we skip the (too long word) */
1173 PICODBG_INFO(("PARSE returned from phoneme extraction with overflow, number of phonemes in phonBuf: %i; forced to TRANSDUCE", spho->phonWritePos));
1174 if ((SPHO_POS_INVALID == spho->activeEndPos) || (spho->activeStartPos == spho->activeEndPos)) {
1175 spho->activeEndPos = spho->headxReadPos;
1176 }
1177 spho->procState = SPHO_STEPSTATE_PROCESS_TRANSDUCE;
1178 break;
1179 } else {
1180 PICODBG_ERROR(("PARSE returned from phoneme extraction with exception %i",rv));
1181 return (picodata_step_result_t)picoos_emRaiseException(this->common->em,
1182 PICO_ERR_OTHER, NULL, NULL);
1183 }
1184 } else {
1185 PICODBG_INFO(("PARSE found other item, passing over"));
1186 /* it is "other" item, ignore */
1187 }
1188 /* set pos at next item */
1189 PICODBG_INFO(("PARSE going to next item: %i -> %i",spho->headxReadPos, spho->headxReadPos + 1));
1190 spho->headxReadPos++;
1191 break;
1192
1193 case SPHO_STEPSTATE_PROCESS_TRANSDUCE:
1194
1195 /* **********************************************************************/
1196 /* TRANSDUCE: transduction input pos/phon pairs to output pos/phon pairs */
1197 /* **********************************************************************/
1198 PICODBG_DEBUG(("TRANSDUCE (%i-th of %i fsts",spho->curFst+1, spho->numFsts));
1199
1200 /* termination condition first */
1201 if (spho->curFst >= spho->numFsts) {
1202
1203 #if defined(PICO_DEBUG)
1204 {
1205 PICODBG_INFO_CTX();
1206 PICODBG_INFO_MSG(("result of all transductions: "));
1207 PICOTRNS_PRINTSYMSEQ(this->voice->kbArray[PICOKNOW_KBID_DBG], spho->phonBufOut, spho->phonWritePos);
1208 PICODBG_INFO_MSG(("\n"));
1209 }
1210 #endif
1211
1212 /* reset for next transduction */
1213 spho->curFst = 0;
1214 /* prepare BOUNDS */
1215 spho->outReadPos = 0;
1216 spho->phonReadPos = 0;
1217
1218 spho->procState = SPHO_STEPSTATE_PROCESS_BOUNDS;
1219 break;
1220 }
1221
1222 /* transduce from phonBufIn to PhonBufOut */
1223 {
1224
1225 picoos_uint32 nrSteps;
1226 #if defined(PICO_DEBUG)
1227 {
1228 PICODBG_INFO_CTX();
1229 PICODBG_INFO_MSG(("spho trying to transduce: "));
1230 PICOTRNS_PRINTSYMSEQ(this->voice->kbArray[PICOKNOW_KBID_DBG], spho->phonBuf, spho->phonWritePos);
1231 PICODBG_INFO_MSG(("\n"));
1232 }
1233 #endif
1234 rv = picotrns_transduce(spho->fst[spho->curFst], FALSE,
1235 picotrns_printSolution, spho->phonBuf, spho->phonWritePos, spho->phonBufOut,
1236 &spho->phonWritePos,
1237 4*PICOTRNS_MAX_NUM_POSSYM, spho->altDescBuf,
1238 spho->maxAltDescLen, &nrSteps);
1239 if (PICO_OK == rv) {
1240 #if defined(PICO_DEBUG)
1241 {
1242 PICODBG_INFO_CTX();
1243 PICODBG_INFO_MSG(("result of transduction: (output symbols: %i)", spho->phonWritePos));
1244 PICOTRNS_PRINTSYMSEQ(this->voice->kbArray[PICOKNOW_KBID_DBG], spho->phonBufOut, spho->phonWritePos);
1245 PICODBG_INFO_MSG(("\n"));
1246 }
1247 #endif
1248 PICODBG_TRACE(("number of steps done in tranduction: %i", nrSteps));
1249 } else {
1250 picoos_emRaiseWarning(this->common->em, PICO_WARN_FALLBACK,NULL,(picoos_char *)"phon buffer full");
1251 }
1252 }
1253 /* eliminate deep epsilons */
1254 picotrns_eliminate_epsilons(spho->phonBufOut, spho->phonWritePos, spho->phonBuf,
1255 &spho->phonWritePos,4*PICOTRNS_MAX_NUM_POSSYM);
1256
1257 spho->curFst++;
1258
1259 /* return PICODATA_PU_ATOMIC */
1260 break;
1261
1262
1263 case SPHO_STEPSTATE_PROCESS_BOUNDS:
1264 /* ************************************************************************/
1265 /* BOUNDS: combine input item with pos/phon pairs to insert/modify bounds */
1266 /* ************************************************************************/
1267
1268 PICODBG_INFO(("BOUNDS"));
1269
1270 /* get the suppressRecombWordBound in the left context */
1271 spho->suppressRecombWordBound = FALSE;
1272 while (spho->outReadPos < spho->activeStartPos) {
1273 /* look at the current item */
1274 ihead = spho->headx[spho->outReadPos].head;
1275 /* icontent = spho->cbuf + spho->headx[spho->outReadPos].cind; */
1276 PICODBG_INFO(("in position %i, looking at item %s",spho->outReadPos,picodata_head_to_string(&ihead,msgstr,SPHO_MSGSTR_SIZE)));
1277 if (PICODATA_ITEM_BOUND == ihead.type) {
1278 spho->suppressRecombWordBound = TRUE;
1279 } else if (PICODATA_ITEM_WORDPHON == ihead.type) {
1280 spho->suppressRecombWordBound = FALSE;
1281 }
1282 spho->outReadPos++;
1283 }
1284 /* spho->outReadPos point now to the active region */
1285
1286 /* advance the phone reading pos to the active range */
1287 spho->phonReadPos = 0;
1288 while (SPHO_POSSYM_OK == (rv = getNextPosSym(spho, &pos, &sym,
1289 spho->activeStartPos))) {
1290 /* ignore */
1291 }
1292 PICODBG_INFO(("skipping left context phones results in %s", (SPHO_POSSYM_OUT_OF_RANGE==rv) ? "OUT_OF_RANGE" : (SPHO_POSSYM_END ==rv) ? "END" : "OTHER"));
1293
1294 /*
1295 * Align input items with transduced phones and note bound stregth changes and break commands
1296 */
1297
1298 while (spho->outReadPos < spho->activeEndPos) {
1299
1300 /* look at the current item */
1301 ihead = spho->headx[spho->outReadPos].head;
1302 icontent = spho->cbuf + spho->headx[spho->outReadPos].cind;
1303 nextInPos = spho->outReadPos + 1;
1304 /* */
1305 PICODBG_INFO(("in position %i, looking at item %s",spho->outReadPos,picodata_head_to_string(&ihead,msgstr,SPHO_MSGSTR_SIZE)));
1306
1307 if ((PICODATA_ITEM_BOUND == ihead.type)
1308 || ((PICODATA_ITEM_WORDPHON == ihead.type)
1309 && (!spho->suppressRecombWordBound))) {
1310 /* there was a boundary originally */
1311 picoos_uint8 orig_strength, orig_type;
1312 if (PICODATA_ITEM_BOUND == ihead.type) {
1313 orig_strength = ihead.info1;
1314 orig_type = ihead.info2;
1315 spho->suppressRecombWordBound = TRUE;
1316 } else {
1317 orig_strength = PICODATA_ITEMINFO1_BOUND_PHR0;
1318 orig_type = PICODATA_ITEMINFO2_NA;
1319 }
1320 /* i expect a boundary phone here */
1321 /* consume FST bound phones, consider pending break and set the side-bound */
1322 PICODBG_INFO(("got BOUND or WORDPHON item and expects corresponding phone"));
1323 rv = getNextPosSym(spho, &pos, &sym, nextInPos);
1324 if (SPHO_POSSYM_OK != rv) {
1325 PICODBG_ERROR(("unexpected symbol or unexpected end of phoneme list (%s)", (SPHO_POSSYM_OUT_OF_RANGE==rv) ? "OUT_OF_RANGE" : (SPHO_POSSYM_END ==rv) ? "END" :"OTHER"));
1326 return (picodata_step_result_t)picoos_emRaiseException(this->common->em,
1327 PICO_ERR_OTHER, NULL, NULL);
1328 }
1329 sym = picotrns_unplane(sym, &plane);
1330 /* */
1331 PICODBG_ASSERT((PICOKFST_PLANE_PB_STRENGTHS == plane));
1332
1333 /* insert modified bound according to transduction and possibly pending break */
1334 setSideBound(spho, orig_strength, orig_type,
1335 (picoos_uint8) sym);
1336 } else if ((PICODATA_ITEM_CMD == ihead.type)
1337 && (PICODATA_ITEMINFO1_CMD_SIL == ihead.info1)) {
1338 /* it's a SIL (break) command */
1339 picoos_uint16 time;
1340 picoos_uint32 pos = 0;
1341 picoos_read_mem_pi_uint16(icontent, &pos, &time);
1342 if (spho->breakPending) {
1343 spho->breakTime += time;
1344 } else {
1345 spho->breakTime = time;
1346 spho->breakPending = TRUE;
1347 }
1348 } else if ((PICODATA_ITEM_CMD == ihead.type) && (PICODATA_ITEMINFO1_CMD_PLAY == ihead.info1)) {
1349 /* insert break of at least one ms */
1350 if (!spho->breakPending || (spho->breakTime <= 0)) {
1351 spho->breakTime = SPHO_SMALLEST_SIL_DUR;
1352 spho->breakPending = TRUE;
1353 }
1354 setSideBound(spho, PICODATA_ITEMINFO1_NA,
1355 PICODATA_ITEMINFO2_NA, PICODATA_ITEMINFO1_NA);
1356 /* force following break to be at least one ms */
1357 spho->breakTime = SPHO_SMALLEST_SIL_DUR;
1358 spho->breakPending = TRUE;
1359 } else if (breakStateInterrupting(&ihead, &breakBefore, &breakAfter)) {
1360
1361 if (breakBefore &&(!spho->breakPending || (spho->breakTime <= 0))) {
1362 spho->breakTime = SPHO_SMALLEST_SIL_DUR;
1363 spho->breakPending = TRUE;
1364 }
1365 setSideBound(spho, PICODATA_ITEMINFO1_NA,
1366 PICODATA_ITEMINFO2_NA, PICODATA_ITEMINFO1_NA);
1367
1368 if (breakAfter) {
1369 spho->breakTime = SPHO_SMALLEST_SIL_DUR;
1370 spho->breakPending = TRUE;
1371 }
1372 if (PICODATA_ITEM_WORDPHON == ihead.type) {
1373 spho->suppressRecombWordBound = FALSE;
1374 }
1375 }
1376
1377 /* skip phones of that item */
1378 while (SPHO_POSSYM_OK == (rv = getNextPosSym(spho, &pos,
1379 &sym, nextInPos))) {
1380 /* ignore */
1381 }
1382 spho->outReadPos++;
1383 }
1384
1385 /* reset for RECOMB */
1386 spho->outReadPos = 0;
1387 spho->phonReadPos = 0;
1388 spho->suppressRecombWordBound = FALSE;
1389
1390 spho->procState = SPHO_STEPSTATE_PROCESS_RECOMB;
1391 return PICODATA_PU_ATOMIC;
1392
1393 break;
1394
1395 case SPHO_STEPSTATE_PROCESS_RECOMB:
1396 /* **********************************************************************/
1397 /* RECOMB: combine input item with pos/phon pairs to output item */
1398 /* **********************************************************************/
1399
1400 PICODBG_TRACE(("RECOMB"));
1401
1402 /* default place to come after feed: here */
1403 spho->feedFollowState = SPHO_STEPSTATE_PROCESS_RECOMB;
1404
1405 /* check termination condition first */
1406 if (spho->outReadPos >= spho->activeEndPos) {
1407 PICODBG_DEBUG(("RECOMB reached active region's end at %i",spho->outReadPos));
1408 spho->procState = SPHO_STEPSTATE_SHIFT;
1409 break;
1410 }
1411
1412 /* look at the current item */
1413 ihead = spho->headx[spho->outReadPos].head;
1414 icontent = spho->cbuf + spho->headx[spho->outReadPos].cind;
1415
1416 PICODBG_DEBUG(("RECOMB looking at item %s",picodata_head_to_string(&ihead,msgstr,SPHO_MSGSTR_SIZE)));
1417
1418 nextInPos = spho->outReadPos + 1;
1419
1420 PICODBG_DEBUG(("RECOMB treating item in headx at pos %i",spho->outReadPos));
1421 if (nextInPos <= spho->activeStartPos) { /* we're in the (passive) left context. Just skip it */
1422 PICODBG_DEBUG(("RECOMB skipping item in the left context (%i <= %i)",nextInPos, spho->activeStartPos));
1423 if (PICODATA_ITEM_BOUND == ihead.type) {
1424 spho->suppressRecombWordBound = 1;
1425 } else if (PICODATA_ITEM_WORDPHON == ihead.type) {
1426 spho->suppressRecombWordBound = 0;
1427 }
1428
1429 /* consume possyms */
1430 while (SPHO_POSSYM_OK == (rv = getNextPosSym(spho,&pos,&sym,nextInPos))) {
1431 /* ignore */
1432 }
1433 if (rv == SPHO_POSSYM_INVALID) {
1434 return (picodata_step_result_t)picoos_emRaiseException(this->common->em,
1435 PICO_ERR_OTHER, NULL, NULL);
1436 }
1437 spho->outReadPos = nextInPos;
1438 } else { /* active region */
1439 if (spho->headx[spho->outReadPos].boundstrength) {
1440 /* ***************** "side-bound" *********************/
1441 /* copy to outbuf */
1442 putSideBoundToOutput(spho);
1443 /* mark as processed */
1444 spho->headx[spho->outReadPos].boundstrength = 0;
1445 /* output it */
1446 spho->procState = SPHO_STEPSTATE_FEED;
1447 } else if (PICODATA_ITEM_BOUND == ihead.type) {
1448 /* ***************** BOUND *********************/
1449 /* expect a boundary phone here */
1450 PICODBG_DEBUG(("RECOMB got BOUND item and expects corresponding phone"));
1451 rv = getNextPosSym(spho, &pos, &sym, nextInPos);
1452 if (SPHO_POSSYM_OK != rv) {
1453 PICODBG_ERROR(("unexpected symbol or unexpected end of phoneme list"));
1454 return (picodata_step_result_t)picoos_emRaiseException(
1455 this->common->em, PICO_ERR_OTHER, NULL,
1456 NULL);
1457 }
1458 sym = picotrns_unplane(sym, &plane);
1459 /* */
1460 PICODBG_ASSERT((PICOKFST_PLANE_PB_STRENGTHS == plane));
1461
1462 spho->suppressRecombWordBound = TRUE; /* if word following, don't need word boundary */
1463 /* just consume item and come back here*/
1464 spho->outReadPos = nextInPos;
1465
1466 } else if (PICODATA_ITEM_WORDPHON == ihead.type) {
1467 /* ***************** WORDPHON *********************/
1468 spho->wordStarted = TRUE;
1469 /* i expect a word boundary symbol in this range unless a phrase boundary was encountered before */
1470 if (spho->suppressRecombWordBound) {
1471 PICODBG_DEBUG(("RECOMB got WORDPHON item but skips expecting BOUND"));
1472 spho->suppressRecombWordBound = FALSE;
1473 } else {
1474 PICODBG_DEBUG(("RECOMB got WORDPHON item and expects corresponding bound phone"));
1475 rv = getNextPosSym(spho, &pos, &sym, nextInPos);
1476 if (SPHO_POSSYM_OK != rv) {
1477 PICODBG_ERROR(("unexpected symbol or unexpected end of phoneme list"));
1478 return (picodata_step_result_t)picoos_emRaiseException(this->common->em,
1479 PICO_ERR_OTHER, NULL, NULL);
1480 }
1481 }
1482 spho->procState = SPHO_STEPSTATE_PROCESS_SYL;
1483 } else if ((PICODATA_ITEM_CMD == ihead.type) && (PICODATA_ITEMINFO1_CMD_SIL == ihead.info1)) {
1484 /* ***************** BREAK COMMAND *********************/
1485 /* just consume and come back here */
1486 PICODBG_DEBUG(("RECOMB consuming item from inBuf %i -> %i",spho->outReadPos, nextInPos));
1487 spho->outReadPos = nextInPos;
1488 } else {
1489 /* ***************** OTHER *********************/
1490 /* just copy item */
1491 PICODBG_DEBUG(("RECOMB found other item, just copying"));
1492 picodata_put_itemparts(&ihead, icontent, ihead.len,
1493 spho->outBuf, spho->outBufSize, &clen);
1494 PICODBG_DEBUG(("RECOMB consuming item from inBuf %i -> %i",spho->outReadPos, nextInPos));
1495 spho->outReadPos = nextInPos;
1496 /* and output it */
1497 spho->procState = SPHO_STEPSTATE_FEED;
1498 } /* if (ihead.type) */
1499
1500 }
1501
1502 /* return PICODATA_PU_BUSY; */
1503 break;
1504
1505 case SPHO_STEPSTATE_PROCESS_SYL:
1506 /* **********************************************************************/
1507 /* SYL: combine input word item with pos/phon pairs to syl output item */
1508 /* **********************************************************************/
1509
1510 /* consume all transduced phonemes with pos in in the range [spho->outReadPos,nextInPos[ */
1511 PICODBG_DEBUG(("SYL"));
1512
1513 spho->feedFollowState = SPHO_STEPSTATE_PROCESS_SYL;
1514
1515 /* look at the current item */
1516 ihead = spho->headx[spho->outReadPos].head;
1517 icontent = spho->cbuf + spho->headx[spho->outReadPos].cind;
1518 nextInPos = spho->outReadPos + 1;
1519 PICODBG_DEBUG(("SYL (1) treating item in headx at pos %i",spho->outReadPos));
1520 /* create syllable item in ohead (head) and sylBuf (contents) */
1521 ohead.type = PICODATA_ITEM_SYLLPHON;
1522
1523 PICODBG_TRACE(("SYL expects accent at phonBuf[%i] = (%i,%i) (outReadPos=%i)", spho->phonReadPos, spho->phonBuf[spho->phonReadPos].pos, spho->phonBuf[spho->phonReadPos].sym,spho->outReadPos));
1524 rv = getNextPosSym(spho,&pos,&sym,nextInPos);
1525 if (SPHO_POSSYM_OK != rv) {
1526 PICODBG_ERROR(("unexpected symbol or unexpected end of phoneme list (%i)",rv));
1527 return (picodata_step_result_t)picoos_emRaiseException(this->common->em, PICO_ERR_OTHER, NULL, NULL);
1528 }
1529 ohead.info2 = picotrns_unplane(sym, &plane);
1530 PICODBG_ASSERT((PICOKFST_PLANE_ACCENTS == plane));
1531 PICODBG_DEBUG(("SYL sets accent to %c", sym));
1532
1533 /* for the time being, we force to use POS so we can transduce all fsts in a row without reconsulting the items */
1534 PICODBG_TRACE(("SYL expects POS"));
1535 PICODBG_DEBUG(("SYL (2) treating item in inBuf range [%i,%i[",spho->outReadPos,nextInPos));
1536 rv = getNextPosSym(spho,&pos,&sym,nextInPos);
1537 if (SPHO_POSSYM_OK != rv) {
1538 PICODBG_ERROR(("unexpected symbol or unexpected end of phoneme list"));
1539 return (picodata_step_result_t)picoos_emRaiseException(this->common->em, PICO_ERR_OTHER, NULL, NULL);
1540 }
1541 if (spho->wordStarted) {
1542 spho->wordStarted = FALSE;
1543 ohead.info1 = picotrns_unplane(sym, &plane);
1544 /* */
1545 PICODBG_ASSERT(PICOKFST_PLANE_POS == plane);
1546 /* */
1547 PICODBG_DEBUG(("SYL setting POS to %c", ohead.info1));
1548 } else {
1549 ohead.info1 = PICODATA_ITEMINFO1_NA;
1550 }
1551
1552 PICODBG_DEBUG(("SYL (3) treating item in inBuf range [%i,%i[",spho->outReadPos,nextInPos));
1553 /* get phonemes of that syllable; stop if syllable boundary or outside word */
1554 sylsym = (PICOKFST_PLANE_PHONEMES << 8)
1555 + spho->syllSepId;
1556 PICODBG_DEBUG(("collecting syllable phonemes before headx position %i",nextInPos));
1557 spho->sylWritePos = 0;
1558 while (SPHO_POSSYM_OK == (rv = getNextPosSym(spho,&pos,&sym,nextInPos)) && (sym != sylsym)) {
1559 spho->sylBuf[spho->sylWritePos++] = picotrns_unplane(sym, &plane);
1560 /* */
1561 PICODBG_TRACE(("SYL adding phoneme to syllable: (pos %i,sym %i)[plane %i,sym %c]",pos,sym,plane,sym & 0xFF));
1562 PICODBG_ASSERT((PICOKFST_PLANE_PHONEMES == plane));
1563 }
1564 PICODBG_DEBUG(("SYL (4) treating item in inBuf range [%i,%i[",spho->outReadPos,nextInPos));
1565 ohead.len = spho->sylWritePos;
1566 if (SPHO_POS_INVALID == rv) {
1567 PICODBG_ERROR(("unexpected symbol or unexpected end of phoneme list"));
1568 return (picodata_step_result_t)picoos_emRaiseException(this->common->em, PICO_WARN_INCOMPLETE, NULL, NULL);
1569 } else if ((SPHO_POSSYM_OUT_OF_RANGE == rv) || (SPHO_POSSYM_END == rv)) {
1570 PICODBG_DEBUG(("SYL arrived at end of word and/or end of phon buffer, go to next word"));
1571 spho->outReadPos = nextInPos; /* advance to next item */
1572 spho->feedFollowState = SPHO_STEPSTATE_PROCESS_RECOMB; /* go to RECOMB after feed */
1573 } else {
1574 PICODBG_ASSERT((sym == sylsym));
1575 }
1576 PICODBG_DEBUG(("SYL (5) treating item in inBuf range [%i,%i[",spho->outReadPos,nextInPos));
1577
1578 if (ohead.len > 0) {
1579 /* prepare syllable output */
1580 picodata_put_itemparts(&ohead, spho->sylBuf,
1581 PICODATA_BUFSIZE_DEFAULT, spho->outBuf,
1582 spho->outBufSize, &clen);
1583
1584 spho->procState = SPHO_STEPSTATE_FEED;
1585 } else { /* skip feeding output of empty syllable */
1586 spho->procState = spho->feedFollowState;
1587 }
1588 break;
1589
1590 case SPHO_STEPSTATE_FEED:
1591 /* **********************************************************************/
1592 /* FEED: output output item and proceed to feedFollowState */
1593 /* **********************************************************************/
1594
1595 PICODBG_DEBUG(("FEED"));
1596
1597 PICODBG_DEBUG(("FEED putting outBuf item into cb"));
1598
1599 /*feeding items to PU output buffer*/
1600 rv = picodata_cbPutItem(this->cbOut, spho->outBuf,
1601 spho->outBufSize, &clen);
1602
1603 PICODATA_INFO_ITEM(this->voice->kbArray[PICOKNOW_KBID_DBG],
1604 (picoos_uint8 *)"spho: ",
1605 spho->outBuf, spho->outBufSize);
1606
1607 if (PICO_EXC_BUF_OVERFLOW == rv) {
1608 /* we have to redo this item */
1609 PICODBG_DEBUG(("FEED got overflow, returning ICODATA_PU_OUT_FULL"));
1610 return PICODATA_PU_OUT_FULL;
1611 } else if (PICO_OK == rv) {
1612 *numBytesOutput += clen;
1613 spho->procState = spho->feedFollowState;
1614 PICODBG_DEBUG(("FEED ok, going back to procState %i", spho->procState));
1615 return PICODATA_PU_BUSY;
1616 } else {
1617 PICODBG_DEBUG(("FEED got exception %i when trying to output item",rv));
1618 spho->procState = spho->feedFollowState;
1619 return (picodata_step_result_t)rv;
1620 }
1621 break;
1622
1623 case SPHO_STEPSTATE_SHIFT:
1624 /* **********************************************************************/
1625 /* SHIFT */
1626 /* **********************************************************************/
1627 /* If there exists a valid penultima, it should replace any left context (from 0 to activeStartPos)
1628 * else discard the current active range (from activeStartPos to activeEndPos), leaving the current
1629 * left context intact. Often, PARSE would move activeStartPos to 0, so that there is no left context
1630 * after the shift.
1631 */
1632
1633 PICODBG_DEBUG(("SHIFT"));
1634
1635 if (spho->penultima != SPHO_POS_INVALID) {
1636 picoos_int16 shift;
1637 /* set penultima as new left context and set activeStartPos to the shifted activeEndPos */
1638 PICODBG_DEBUG((
1639 "SHIFT shifting penultima from %i to 0",
1640 spho->penultima));
1641 shift = shift_range_left_1(spho, &spho->penultima, 0);
1642 if (shift < 0) {
1643 picoos_emRaiseException(this->common->em,PICO_ERR_OTHER,NULL,NULL);
1644 return PICODATA_PU_ERROR;
1645 }
1646 spho->activeStartPos = spho->activeEndPos
1647 - shift;
1648 spho->lastPhraseBoundPos -= shift;
1649 spho->suppressParseWordBound = FALSE;
1650 spho->suppressRecombWordBound = FALSE;
1651
1652 } else {
1653 picoos_int16 shift;
1654 picoos_bool lastPhraseBoundActive;
1655 if (spho->activeStartPos == spho->activeEndPos) {
1656 /* no items consumed; we have to abandon left context */
1657 spho->activeStartPos = 0;
1658 }
1659 lastPhraseBoundActive = (spho->lastPhraseBoundPos >= spho->activeStartPos);
1660 /* dummy comment */
1661 PICODBG_DEBUG(("SHIFT shift active end from %i to %i",
1662 spho->activeEndPos, spho->activeStartPos));
1663 shift = shift_range_left_1(spho, &spho->activeEndPos, spho->activeStartPos);
1664 if (shift < 0) {
1665 picoos_emRaiseException(this->common->em,PICO_ERR_OTHER,NULL,NULL);
1666 return PICODATA_PU_ERROR;
1667 }
1668 if (lastPhraseBoundActive) {
1669 spho->lastPhraseBoundPos -= shift;
1670 }
1671 }
1672
1673 spho->procState = SPHO_STEPSTATE_INIT;
1674 break;
1675
1676 default:
1677 picoos_emRaiseException(this->common->em, PICO_ERR_OTHER, NULL, NULL);
1678 return PICODATA_PU_ERROR;
1679 break;
1680
1681 } /* switch (spho->procState) */
1682
1683 } /* while (1) */
1684
1685 /* should be never reached */
1686 picoos_emRaiseException(this->common->em, PICO_ERR_OTHER, NULL, NULL);
1687 return PICODATA_PU_ERROR;
1688 }
1689
1690 #ifdef __cplusplus
1691 }
1692 #endif
1693
1694 /* end picospho.c */
1695