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 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 289 static pico_status_t sphoInitialize(register picodata_ProcessingUnit this, picoos_int32 resetMode) 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 336 static pico_status_t sphoTerminate(register picodata_ProcessingUnit this) 337 { 338 return PICO_OK; 339 } 340 341 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 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 */ 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 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 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 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) */ 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 /* ***********************************************************************/ 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 */ 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 */ 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 */ 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 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 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 */ 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 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