| /* |
| * Copyright (C) 2008-2009 SVOX AG, Baslerstr. 30, 8048 Zuerich, Switzerland |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| /** |
| * @file picospho.c |
| * |
| * sentence phonemic/phonetic FSTs PU |
| * |
| * Copyright (C) 2008-2009 SVOX AG, Baslerstr. 30, 8048 Zuerich, Switzerland |
| * All rights reserved. |
| * |
| * History: |
| * - 2009-04-20 -- initial version |
| * |
| */ |
| |
| #include "picoos.h" |
| #include "picodbg.h" |
| #include "picodata.h" |
| |
| #include "picoknow.h" |
| #include "picokfst.h" |
| #include "picoktab.h" |
| #include "picotrns.h" |
| |
| #include "picospho.h" |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| #if 0 |
| } |
| #endif |
| |
| #define SPHO_BUFSIZE (3 * PICODATA_BUFSIZE_DEFAULT) |
| |
| |
| |
| #define SPHO_MAX_ALTDESC_SIZE (60 * PICOTRNS_MAX_NUM_POSSYM) |
| |
| |
| #define SPHO_SMALLEST_SIL_DUR 1 |
| |
| |
| /** @addtogroup picospho |
| * |
| * Algorithmic description |
| * ======================= |
| * The main function, sphoStep, is divided into the subprocesses (processing states) described further down. |
| * |
| * Flow control: |
| * ------------ |
| * The processing flow is controlled by setting |
| * - 'procState' : the next state to be processed |
| * - 'feedFollowState' : the state to be processed after the feed state (the feed state is treated like a primitive "subroutine") |
| * - some other flags |
| * |
| * Buffering: |
| * --------- |
| * - The input items are mainly stored and processed in two buffers, collectively called 'inBuf' |
| * - cbuf : unstructured buffer containing item contents |
| * - headx : structured buffer containing item heads, each expanded by a pointer to the item contents |
| * and space for a boundary potentially to be inserted (to the left of the original item) |
| * - For transduction, phonemes and their position are extracted from inBuf into |
| * - phonBuf, |
| * processed there, and the resulting phonemes realigned with inBuf. |
| * - Word items are split into syllables, stored in |
| * - sylBuf |
| * - Items to be output are stored in outBuf |
| * |
| * Windowing: |
| * --------- |
| * Optimal solutions are achieved if a whole sentence is processed at once. However, if any of the buffers are too small, |
| * only sentence parts are processed. To improve the quality of such sub-optimal solutions, a moving-window-with-overlap is applied: |
| * - [0,headxReadPos[ : the window considered for transduction |
| * - [activeStartPos,activeEndPos[ : the "active" subrange of the window actually used for output |
| * - penultima : the position (within the active range) that should be used as new window start when shifting the window |
| * |
| * After PROCESS_PARSE: |
| * 0 activeStartPos penultima activeEndPos headxReadPos headxWritePos |
| * | | | | | | |
| * |-------------=================================---------------| ['----': context '====' : active subrange) |
| * |
| * After PROCESS_SHIFT: |
| * 0 activeStartPos headWritePos |
| * | | | | |
| * |------------... (only left context is known; new active range, penultima, and right context to be established at next parse) |
| * |
| * Processing states: |
| * ----------------- |
| * - INIT : initialize state variables |
| * - COLLECT : collect items into internal buffers ("inBuf") |
| * - PROCESS_PARSE : go through inBuf items and extract position/phoneme pairs into phoneme buffer 'phonBuf' |
| * word boundary phonemes are inserted between words |
| * - PROCESS_TRANSDUCE : transduce phonBuf |
| * - PROCESS_BOUNDS : go through inBuf items again and match against transduced pos/phoneme |
| * this is the first round of alignment, only inserting/deleting/modifying bounds, according to |
| * - existing BOUND items |
| * - newly produced word bounds separating WORDPHON items |
| * - bound upgrades/downgrades from transduction |
| * - bound upgrades/downgrades/insertions from SIL command items (originating e.g. from <break> text commands) |
| * all relevant bounds are placed in the corresponding headx extention; original bound items become invalid. |
| * - PROCESS_RECOMB : go through inBuf items again and match against transduced pos/phoneme |
| * this is the second round of alignment, treating non-BOUND items |
| * - WORDPHONs are broken into syllables by "calling" PROCESS_SYL |
| * - "side-bounds" (in the headx extension) are output by "calling" FEED |
| * - BOUND items are consumed with no effect |
| * - other items are output unchanged "calling" FEED |
| * - PROCESS_SYL : the WORDPHON coming from RECOMB is matched against the phonBuf and (new) SYLLPHON items |
| * are created. (the original wordphon is consumed) |
| * - FEED : feeds one item and returns to spho->feedFollowState |
| * - SHIFT : items in inBuf are shifted left to make room for new items. If a sentence doesn't fit |
| * inBuf in its entirety, left and/or right contexts are kept so they can be considered in |
| * the next transduction. |
| */ |
| |
| |
| |
| /* PU sphoStep states */ |
| #define SPHO_STEPSTATE_INIT 0 |
| #define SPHO_STEPSTATE_COLLECT 1 |
| #define SPHO_STEPSTATE_PROCESS_PARSE 2 |
| #define SPHO_STEPSTATE_PROCESS_TRANSDUCE 3 |
| #define SPHO_STEPSTATE_PROCESS_BOUNDS 4 |
| #define SPHO_STEPSTATE_PROCESS_RECOMB 5 |
| #define SPHO_STEPSTATE_PROCESS_SYL 6 |
| #define SPHO_STEPSTATE_FEED 7 |
| #define SPHO_STEPSTATE_SHIFT 8 |
| |
| #define SPHO_POS_INVALID (PICOTRNS_POS_INVALID) /* indicates that no position was set yet */ |
| |
| /* nr item restriction: maximum number of extended item heads in headx */ |
| #define SPHO_MAXNR_HEADX 60 |
| |
| /* nr item restriction: maximum size of all item contents together in cont */ |
| #define SPHO_MAXSIZE_CBUF (30 * 255) |
| |
| /* "expanded head": item head expanded by a content position and a by boundary information |
| * potentially inserted "to the left" of the item */ |
| typedef struct { |
| picodata_itemhead_t head; |
| picoos_uint16 cind; |
| picoos_uint8 boundstrength; /* bstrength to the left, 0 if not set */ |
| picoos_uint8 phrasetype; /* btype for following phrase, 0 if not set */ |
| picoos_int16 sildur; /* silence duration for boundary, -1 if not set */ |
| } picospho_headx_t; |
| |
| |
| |
| #define SPHO_MSGSTR_SIZE 32 |
| |
| /** object : SentPhoUnit |
| * shortcut : spho |
| * derived from : picodata_ProcessingUnit |
| */ |
| typedef struct spho_subobj { |
| picoos_Common common; |
| |
| /* we use int16 for buffer positions so we can indicate exceptional positions (invalid etc.) with negative |
| * integers */ |
| picoos_uint8 procState; /* for next processing step decision */ |
| |
| /* buffer for item headers */ |
| picoos_uint8 tmpbuf[PICODATA_MAX_ITEMSIZE]; /* tmp. location for an item */ |
| |
| picospho_headx_t headx[SPHO_MAXNR_HEADX]; /* "expanded head" buffer */ |
| picoos_uint16 headxBufSize; /* actually allocated size (if one day headxBuf is allocated dynamically) */ |
| picoos_uint16 headxReadPos, headxWritePos; |
| |
| picoos_uint8 cbuf[SPHO_MAXSIZE_CBUF]; |
| picoos_uint16 cbufBufSize; /* actually allocated size */ |
| picoos_uint16 cbufWritePos; /* next position to write to, 0 if buffer empty */ |
| |
| picoos_uint8 outBuf[PICODATA_BUFSIZE_DEFAULT]; /* internal output buffer to hold just one item */ |
| picoos_uint16 outBufSize; /* actually allocated size (if one day outBuf is allocated dynamically) */ |
| picoos_uint16 outReadPos; /* next pos to read from inBuf for output */ |
| |
| /* picoos_int16 outWritePos; */ /* next pos to output from in buf */ |
| |
| picoos_uint8 sylBuf[255]; /* internal buffer to hold contents of syl item to be output */ |
| picoos_uint8 sylReadPos, sylWritePos; /* next pos to read from sylBuf, next pos to write to sylBuf */ |
| |
| /* buffer for internal calculation of transducer */ |
| picotrns_AltDesc altDescBuf; |
| /* the number of AltDesc in the buffer */ |
| picoos_uint16 maxAltDescLen; |
| |
| /* the input to a transducer should not be larger than PICOTRNS_MAX_NUM_POSSYM |
| * so the output may expand (up to 4*PICOTRNS_MAX_NUM_POSSYM) */ |
| |
| picotrns_possym_t phonBufA[4 * PICOTRNS_MAX_NUM_POSSYM + 1]; |
| picotrns_possym_t phonBufB[4 * PICOTRNS_MAX_NUM_POSSYM + 1]; |
| picotrns_possym_t * phonBuf; |
| picotrns_possym_t * phonBufOut; |
| picoos_uint16 phonReadPos, phonWritePos; /* next pos to read from phonBufIn, next pos to write to phonBufIn */ |
| |
| picoos_int16 activeStartPos; /* start position of items to be treated (at end of left context) */ |
| picoos_int16 penultima, activeEndPos; /* positions of last two bounds/words; SPHO_POS_INVALID means uninitialized */ |
| picoos_int16 lastPhraseBoundPos; /* position of the last bound encountered (<0 if inexistent or not reachable */ |
| picoos_uint8 lastPhraseType; /* phrase type of the last phrase boundary, 0 if not set */ |
| |
| picoos_uint8 needMoreInput, /* more data necessary to decide on token */ |
| suppressParseWordBound, /* dont produce word boundary */ |
| suppressRecombWordBound, /* dont produce word boundary */ |
| breakPending, /* received a break but didn't interpret it yet */ |
| /* sentEnd, */ /* sentence end detected */ |
| force, /* in forced state */ |
| wordStarted, /* is it the first syl in the word: expect POS */ |
| sentenceStarted; |
| |
| picoos_uint16 breakTime; /* time argument of the pending break command */ |
| |
| picoos_uint8 feedFollowState; /* where to return after feed */ |
| |
| /* fst knowledge bases */ |
| picoos_uint8 numFsts; |
| picokfst_FST fst[PICOKNOW_MAX_NUM_SPHO_FSTS]; |
| picoos_uint8 curFst; /* the fst to be applied next */ |
| |
| /* fixed ids knowledge base */ |
| picoktab_FixedIds fixedIds; |
| |
| /* phones kb */ |
| picoktab_Phones phones; |
| |
| /* some soecial ids from phones */ |
| picoos_uint8 primStressId, secondStressId, syllSepId; |
| |
| } spho_subobj_t; |
| |
| |
| static pico_status_t sphoReset(register picodata_ProcessingUnit this) |
| { |
| |
| spho_subobj_t * spho; |
| |
| if (NULL == this || NULL == this->subObj) { |
| return picoos_emRaiseException(this->common->em, |
| PICO_ERR_NULLPTR_ACCESS, NULL, NULL); |
| } |
| spho = (spho_subobj_t *) this->subObj; |
| |
| spho->curFst = 0; |
| |
| /* processing state */ |
| spho->procState = SPHO_STEPSTATE_INIT; |
| spho->needMoreInput = TRUE; |
| spho->suppressParseWordBound = FALSE; |
| spho->suppressRecombWordBound = FALSE; |
| spho->breakPending = FALSE; |
| spho->force = 0; |
| spho->sentenceStarted = 0; |
| |
| |
| /* item buffer headx/cbuf */ |
| spho->headxBufSize = SPHO_MAXNR_HEADX; |
| spho->headxReadPos = 0; |
| spho->headxWritePos = 0; |
| |
| spho->cbufWritePos = 0; |
| spho->cbufBufSize = SPHO_MAXSIZE_CBUF; |
| |
| /* possym buffer */ |
| spho->phonBuf = spho->phonBufA; |
| spho->phonBufOut = spho->phonBufB; |
| spho->phonReadPos = 0; |
| |
| /* overlapping */ |
| spho->activeStartPos = 0; |
| spho->penultima = SPHO_POS_INVALID; |
| spho->activeEndPos = SPHO_POS_INVALID; |
| |
| return PICO_OK; |
| } |
| |
| |
| static pico_status_t sphoInitialize(register picodata_ProcessingUnit this, picoos_int32 r_mode) |
| { |
| picoos_uint8 i; |
| spho_subobj_t * spho; |
| picokfst_FST fst; |
| |
| picoknow_kb_id_t myKbIds[PICOKNOW_MAX_NUM_SPHO_FSTS] = PICOKNOW_KBID_SPHO_ARRAY; |
| |
| PICODBG_DEBUG(("init")); |
| |
| if (NULL == this || NULL == this->subObj) { |
| return picoos_emRaiseException(this->common->em, |
| PICO_ERR_NULLPTR_ACCESS, NULL, NULL); |
| } |
| |
| spho = (spho_subobj_t *) this->subObj; |
| |
| spho->numFsts = 0; |
| |
| spho->curFst = 0; |
| |
| for (i = 0; i<PICOKNOW_MAX_NUM_SPHO_FSTS; i++) { |
| fst = picokfst_getFST(this->voice->kbArray[myKbIds[i]]); |
| if (NULL != fst) { |
| spho->fst[spho->numFsts++] = fst; |
| } |
| } |
| spho->fixedIds = picoktab_getFixedIds(this->voice->kbArray[PICOKNOW_KBID_FIXED_IDS]); |
| spho->phones = picoktab_getPhones(this->voice->kbArray[PICOKNOW_KBID_TAB_PHONES]); |
| |
| spho->syllSepId = picoktab_getSyllboundID(spho->phones); |
| spho->primStressId = picoktab_getPrimstressID(spho->phones); |
| spho->secondStressId = picoktab_getSecstressID(spho->phones); |
| |
| PICODBG_DEBUG(("got %i fsts", spho->numFsts)); |
| |
| |
| return sphoReset(this); |
| |
| } |
| |
| static picodata_step_result_t sphoStep(register picodata_ProcessingUnit this, |
| picoos_int16 mode, picoos_uint16 *numBytesOutput); |
| |
| |
| |
| |
| static pico_status_t sphoTerminate(register picodata_ProcessingUnit this) |
| { |
| return PICO_OK; |
| } |
| |
| |
| static pico_status_t sphoSubObjDeallocate(register picodata_ProcessingUnit this, |
| picoos_MemoryManager mm) |
| { |
| spho_subobj_t * spho; |
| |
| spho = (spho_subobj_t *) this->subObj; |
| |
| if (NULL != this) { |
| if (NULL != this->subObj) { |
| spho = (spho_subobj_t *) (this->subObj); |
| picotrns_deallocate_alt_desc_buf(spho->common->mm,&spho->altDescBuf); |
| picoos_deallocate(mm, (void *) &this->subObj); |
| } |
| } |
| return PICO_OK; |
| } |
| |
| picodata_ProcessingUnit picospho_newSentPhoUnit(picoos_MemoryManager mm, |
| picoos_Common common, picodata_CharBuffer cbIn, |
| picodata_CharBuffer cbOut, picorsrc_Voice voice) |
| { |
| spho_subobj_t * spho; |
| |
| picodata_ProcessingUnit this = picodata_newProcessingUnit(mm, common, cbIn, cbOut, voice); |
| if (this == NULL) { |
| return NULL; |
| } |
| |
| this->initialize = sphoInitialize; |
| this->step = sphoStep; |
| this->terminate = sphoTerminate; |
| this->subDeallocate = sphoSubObjDeallocate; |
| |
| this->subObj = picoos_allocate(mm, sizeof(spho_subobj_t)); |
| if (this->subObj == NULL) { |
| picoos_deallocate(mm, (void **)(void*)&this); |
| return NULL; |
| } |
| spho = (spho_subobj_t *) this->subObj; |
| |
| spho->common = this->common; |
| |
| /* these are given by the pre-allocated array sizes */ |
| spho->outBufSize = PICODATA_BUFSIZE_DEFAULT; |
| |
| |
| spho->altDescBuf = picotrns_allocate_alt_desc_buf(spho->common->mm, SPHO_MAX_ALTDESC_SIZE, &spho->maxAltDescLen); |
| if (NULL == spho->altDescBuf) { |
| picotrns_deallocate_alt_desc_buf(spho->common->mm,&spho->altDescBuf); |
| picoos_emRaiseException(spho->common->em,PICO_EXC_OUT_OF_MEM, NULL,NULL); |
| return NULL; |
| } |
| |
| sphoInitialize(this, PICO_RESET_FULL); |
| return this; |
| } |
| |
| |
| /* ***********************************************************************/ |
| /* process buffered item list */ |
| /* ***********************************************************************/ |
| |
| |
| /* shift relevant data in headx/'cbuf' (between 'readPos' incl and writePos non-incl) to 'start'. |
| * modify read/writePos accordingly */ |
| static picoos_int16 shift_range_left_1(spho_subobj_t *spho, picoos_int16 * from, picoos_int16 to) |
| { |
| |
| /* remember shift parameters for cbuf */ |
| picoos_uint16 |
| c_i, |
| c_j, |
| c_diff, |
| c_writePos, |
| i, |
| j, |
| diff, |
| writePos; |
| i = to; |
| j = *from; |
| diff = j-i; |
| writePos = spho->headxWritePos; |
| c_i = spho->headx[to].cind; |
| if (j < writePos) { |
| c_j = spho->headx[j].cind; |
| } else { |
| c_j = spho->cbufWritePos; |
| } |
| c_diff = c_j - c_i; |
| c_writePos = spho->cbufWritePos; |
| |
| PICODBG_DEBUG(( |
| "shifting buffer region [%i,%i[ down to %i",*from, writePos, to |
| )); |
| |
| |
| /* PICODBG_ASSERT((i<j)); */ |
| if (i > j) { |
| return -1; |
| } |
| /* shift cbuf */ |
| while (c_j < c_writePos) { |
| spho->cbuf[c_i++] = spho->cbuf[c_j++]; |
| } |
| /* shift headx */ |
| while (j < writePos) { |
| spho->headx[j].cind -= c_diff; |
| spho->headx[i++] = spho->headx[j++]; |
| } |
| spho->headxWritePos -= diff; |
| *from = to; |
| spho->cbufWritePos -= c_diff; |
| /* */ |
| PICODBG_DEBUG(( |
| "readPos,WritePos are now [%i,%i[, returning shift amount %i",*from, spho->headxWritePos, diff |
| )); |
| return diff; |
| } |
| |
| static pico_status_t sphoAddPhoneme(register spho_subobj_t *spho, picoos_int16 pos, picoos_int16 sym) { |
| picoos_uint8 plane, unshifted; |
| /* just for debuging */ |
| unshifted = picotrns_unplane(sym,&plane); |
| PICODBG_TRACE(("adding %i/%i (%c on plane %i) at phonBuf[%i]",pos,sym,unshifted,plane,spho->phonWritePos)); |
| if (2* PICOTRNS_MAX_NUM_POSSYM <= spho->phonWritePos) { |
| /* not an error! */ |
| PICODBG_DEBUG(("couldn't add because phon buffer full")); |
| return PICO_EXC_BUF_OVERFLOW; |
| } else { |
| spho->phonBuf[spho->phonWritePos].pos = pos; |
| spho->phonBuf[spho->phonWritePos].sym = sym; |
| spho->phonWritePos++; |
| return PICO_OK; |
| } |
| } |
| |
| static pico_status_t sphoAddStartPhoneme(register spho_subobj_t *spho) { |
| return sphoAddPhoneme(spho, PICOTRNS_POS_IGNORE, |
| (PICOKFST_PLANE_INTERN << 8) + spho->fixedIds->phonStartId); |
| } |
| |
| static pico_status_t sphoAddTermPhonemes(register spho_subobj_t *spho, picoos_uint16 pos) { |
| return sphoAddPhoneme(spho, pos, |
| (PICOKFST_PLANE_PB_STRENGTHS << 8) + PICODATA_ITEMINFO1_BOUND_SEND) |
| && sphoAddPhoneme(spho, PICOTRNS_POS_IGNORE, |
| (PICOKFST_PLANE_INTERN << 8) + spho->fixedIds->phonTermId); |
| } |
| |
| /* return "syllable accent" (or prominence) symbol, given "word accent" symbol 'wacc' and stress value (no=0, primary=1, secondary=2) */ |
| static picoos_uint16 sphoGetSylAccent(register spho_subobj_t *spho, |
| picoos_uint8 wacc, picoos_uint8 sylStress) |
| { |
| PICODBG_ASSERT(sylStress <= 2); |
| |
| spho = spho; /* avoid warning "var not used in this function"*/ |
| |
| switch (sylStress) { |
| case 0: /* non-stressed syllable gets no prominence */ |
| /* return spho->fixedIds->accId[0]; */ |
| return PICODATA_ACC0; |
| break; |
| case 1: /* primary-stressed syllable gets word prominence */ |
| return wacc; |
| break; |
| case 2: /* secondary-stressed syllable gets no prominence or secondary stress prom. (4) */ |
| return (PICODATA_ACC0 == wacc) ? PICODATA_ACC0 |
| : PICODATA_ACC4; |
| /*return (spho->fixedIds->accId[0] == wacc) ? spho->fixedIds->accId[0] |
| : spho->fixedIds->accId[4]; */ |
| break; |
| default: |
| /* never occurs :-) */ |
| return PICODATA_ACC0; |
| break; |
| } |
| } |
| |
| |
| /* ***********************************************************************/ |
| /* extract phonemes of an item into a phonBuf */ |
| /* ***********************************************************************/ |
| static pico_status_t sphoExtractPhonemes(register picodata_ProcessingUnit this, |
| register spho_subobj_t *spho, picoos_uint16 pos, |
| picoos_uint8 convertAccents, picoos_uint8 * suppressWB) |
| { |
| pico_status_t rv = PICO_OK; |
| picoos_uint16 i, j; |
| picoos_int16 fstSymbol; |
| picoos_uint8 curStress; |
| picotrns_possym_t tmpPosSym; |
| picoos_uint16 oldPos, curPos; |
| picodata_itemhead_t * head; |
| picoos_uint8* content; |
| |
| #if defined(PICO_DEBUG) |
| picoos_char msgstr[SPHO_MSGSTR_SIZE]; |
| #endif |
| |
| |
| /* |
| Items considered in a transduction are a BOUND or a WORDPHON item. its starting offset within the |
| headxBuf is given as 'pos'. |
| Elements that go into the transduction receive "their" position in the buffer. |
| */ |
| |
| oldPos = spho->phonWritePos; |
| |
| head = &(spho->headx[pos].head); |
| content = spho->cbuf + spho->headx[pos].cind; |
| |
| PICODBG_TRACE(("doing item %s\n", |
| picodata_head_to_string(head,msgstr,SPHO_MSGSTR_SIZE))); |
| |
| switch (head->type) { |
| case PICODATA_ITEM_BOUND: |
| /* map SBEG, SEND and TERM (as sentence closing) to SEND */ |
| fstSymbol = (PICODATA_ITEMINFO1_BOUND_SBEG == head->info1 || PICODATA_ITEMINFO1_BOUND_TERM == head->info1) ? PICODATA_ITEMINFO1_BOUND_SEND : head->info1; |
| PICODBG_TRACE(("found bound of type %c\n",head->info1)); |
| /* BOUND(<bound strength><phrase type>) */ |
| /* insert bound strength */ |
| PICODBG_TRACE(("inserting phrase bound phoneme %c and setting suppresWB=1\n",fstSymbol)); |
| fstSymbol += (PICOKFST_PLANE_PB_STRENGTHS << 8); |
| rv = sphoAddPhoneme(spho,pos,fstSymbol); |
| /* phrase type not used */ |
| /* suppress next word boundary */ |
| (*suppressWB) = 1; |
| break; |
| |
| case PICODATA_ITEM_WORDPHON: |
| /* WORDPHON(POS,WACC)phon */ |
| PICODBG_TRACE(("found WORDPHON")); |
| /* insert word boundary if not suppressed */ |
| if (!(*suppressWB)) { |
| fstSymbol = (PICOKFST_PLANE_PB_STRENGTHS << 8) + PICODATA_ITEMINFO1_BOUND_PHR0; |
| PICODBG_TRACE(("adding word boundary phone")); |
| rv = sphoAddPhoneme(spho,pos,fstSymbol); |
| } |
| (*suppressWB) = 0; |
| /* for the time being, we force to use POS so we can transduce all fsts in a row without reconsulting the items */ |
| |
| |
| /* If 'convertAccents' then the accentuation is not directly encoded. It rather influences the mapping of |
| * the word accent symbol to the actual accent phoneme which is put after the syllable separator. */ |
| if (convertAccents) { |
| PICODBG_TRACE(("converting accents")); |
| /* extracting phonemes IN REVERSE order replacing syllable symbols with prominence symbols */ |
| curPos = spho->phonWritePos; |
| curStress = 0; /* no stress */ |
| for (i = head->len; i > 0 ;) { |
| i--; |
| if (spho->primStressId == content[i]) { |
| curStress = 1; |
| PICODBG_DEBUG(("skipping primary stress at pos %i (in 1 .. %i)",i, head->len)); |
| continue; /* skip primary stress symbol */ |
| } else if (spho->secondStressId == content[i]) { |
| curStress = 2; |
| PICODBG_DEBUG(("skipping secondary stress at pos %i (in 1 .. %i)",i, head->len)); |
| continue; /* skip secundary stress symbol */ |
| } else if (spho->syllSepId == content[i]) { |
| fstSymbol = (PICOKFST_PLANE_POS << 8) + head->info1; |
| rv = sphoAddPhoneme(spho, pos, fstSymbol); |
| /* replace syllSepId by combination of syllable stress and word prominence */ |
| fstSymbol = sphoGetSylAccent(spho,head->info2,curStress); |
| curStress = 0; |
| /* add accent */ |
| fstSymbol += (PICOKFST_PLANE_ACCENTS << 8); |
| rv = sphoAddPhoneme(spho,pos,fstSymbol); |
| if (PICO_OK != rv) { |
| break; |
| } |
| /* and keep syllable boundary */ |
| fstSymbol = (PICOKFST_PLANE_PHONEMES << 8) + content[i]; |
| } else { |
| /* normal phoneme */ |
| fstSymbol = (PICOKFST_PLANE_PHONEMES << 8) + content[i]; |
| } |
| if (PICO_OK == rv) { |
| rv = sphoAddPhoneme(spho,pos,fstSymbol); |
| } |
| } |
| if (PICO_OK == rv) { |
| /* bug 366: we position the "head" into the item header and not on the first phoneme |
| * because there might be no phonemes at all */ |
| /* insert head of the first syllable of a word */ |
| fstSymbol = (PICOKFST_PLANE_POS << 8) + head->info1; |
| rv = sphoAddPhoneme(spho,pos,fstSymbol); |
| fstSymbol = sphoGetSylAccent(spho,head->info2,curStress); |
| curStress = 0; |
| fstSymbol += (PICOKFST_PLANE_ACCENTS << 8); |
| rv = sphoAddPhoneme(spho,pos,fstSymbol); |
| } |
| if (PICO_OK == rv) { |
| /* invert sympos portion */ |
| i = curPos; |
| j=spho->phonWritePos-1; |
| while (i < j) { |
| tmpPosSym.pos = spho->phonBuf[i].pos; |
| tmpPosSym.sym = spho->phonBuf[i].sym; |
| spho->phonBuf[i].pos = spho->phonBuf[j].pos; |
| spho->phonBuf[i].sym = spho->phonBuf[j].sym; |
| spho->phonBuf[j].pos = tmpPosSym.pos; |
| spho->phonBuf[j].sym = tmpPosSym.sym; |
| i++; |
| j--; |
| } |
| } |
| } else { /* convertAccents */ |
| for (i = 0; i <head->len; i++) { |
| fstSymbol = (PICOKFST_PLANE_PHONEMES << 8) + content[i]; |
| rv = sphoAddPhoneme(spho,pos,fstSymbol); |
| } |
| } |
| break; |
| default: |
| picoos_emRaiseException(this->common->em,rv,NULL,NULL); |
| break; |
| } /* switch(head->type) */ |
| if (PICO_OK != rv) { |
| spho->phonWritePos = oldPos; |
| } |
| return rv; |
| } |
| |
| |
| |
| |
| |
| #define SPHO_POSSYM_OK 0 |
| #define SPHO_POSSYM_OUT_OF_RANGE 1 |
| #define SPHO_POSSYM_END 2 |
| #define SPHO_POSSYM_INVALID -3 |
| /* *readPos is the next position in phonBuf to be read, and *writePos is the first position not to be read (may be outside |
| * buf). |
| * 'rangeEnd' is the first possym position outside the desired range. |
| * Possible return values: |
| * SPHO_POSSYM_OK : 'pos' and 'sym' are set to the read possym, *readPos is advanced |
| * SPHO_POSSYM_OUT_OF_RANGE : pos is out of range. 'pos' is set to that of the read possym, 'sym' is undefined |
| * SPHO_POSSYM_UNDERFLOW : no more data in buf. 'pos' is set to PICOTRNS_POS_INVALID, 'sym' is undefined |
| * SPHO_POSSYM_INVALID : "strange" pos. 'pos' is set to PICOTRNS_POS_INVALID, 'sym' is undefined |
| */ |
| static pico_status_t getNextPosSym(spho_subobj_t * spho, picoos_int16 * pos, picoos_int16 * sym, |
| picoos_int16 rangeEnd) { |
| /* skip POS_IGNORE */ |
| while ((spho->phonReadPos < spho->phonWritePos) && (PICOTRNS_POS_IGNORE == spho->phonBuf[spho->phonReadPos].pos)) { |
| PICODBG_DEBUG(("ignoring phone at spho->phonBuf[%i] because it has pos==IGNORE",spho->phonReadPos)); |
| spho->phonReadPos++; |
| } |
| if ((spho->phonReadPos < spho->phonWritePos)) { |
| *pos = spho->phonBuf[spho->phonReadPos].pos; |
| if ((PICOTRNS_POS_INSERT == *pos) || ((0 <= *pos) && (*pos < rangeEnd))) { |
| *sym = spho->phonBuf[spho->phonReadPos++].sym; |
| return SPHO_POSSYM_OK; |
| } else if (*pos < 0){ /* *pos is "strange" (e.g. POS_INVALID) */ |
| return SPHO_POSSYM_INVALID; |
| } else { |
| return SPHO_POSSYM_OUT_OF_RANGE; |
| } |
| } else { |
| /* no more possyms to read */ |
| *pos = PICOTRNS_POS_INVALID; |
| return SPHO_POSSYM_END; |
| } |
| } |
| |
| |
| |
| /** Calculate bound strength modified by transduction |
| * |
| * Given the original bound strength 'orig' and the desired target strength 'target' (suggested by fst), |
| * calculate the modified bound strength. |
| * |
| * @param orig original bound strength |
| * @param target target bound strength |
| * @return resulting bound strength |
| */ |
| static picoos_uint8 fstModifiedBoundStrength(picoos_uint8 orig, picoos_uint8 target) |
| { |
| switch (orig) { |
| case PICODATA_ITEMINFO1_BOUND_PHR1: |
| case PICODATA_ITEMINFO1_BOUND_PHR2: |
| /* don't allow primary phrase bounds to be demoted to word bound */ |
| if (PICODATA_ITEMINFO1_BOUND_PHR0 == target) { |
| return PICODATA_ITEMINFO1_BOUND_PHR3; |
| } |
| case PICODATA_ITEMINFO1_BOUND_PHR0: |
| case PICODATA_ITEMINFO1_BOUND_PHR3: |
| return target; |
| break; |
| default: |
| /* don't allow bounds other than phrase or word bounds to be changed */ |
| return orig; |
| break; |
| } |
| } |
| |
| /** Calculate bound strength modified by a \<break> command |
| * |
| * Given the original (predicted and possibly fst-modified) bound strength, and a time value from an |
| * overwriding \<break> command, calculate the modified bound strength. |
| * |
| * @param orig original bound strength |
| * @param time time given as property of \<break> command |
| * @param wasPrimary |
| * @return modified bound strength |
| */ |
| static picoos_uint8 breakModifiedBoundStrength(picoos_uint8 orig, picoos_uint16 time, picoos_bool wasPrimary) |
| { |
| picoos_uint8 modified = (0 == time) ? PICODATA_ITEMINFO1_BOUND_PHR3 : |
| (50 < time) ? PICODATA_ITEMINFO1_BOUND_PHR1 : PICODATA_ITEMINFO1_BOUND_PHR2; |
| switch (orig) { |
| /* for word and phrase breaks, return 'modified', unless a non-silence gets time==0, in which |
| * case return no break (word break) */ |
| case PICODATA_ITEMINFO1_BOUND_PHR0: |
| if (0 == time) { |
| return PICODATA_ITEMINFO1_BOUND_PHR0; |
| } |
| case PICODATA_ITEMINFO1_BOUND_PHR3: |
| if (!wasPrimary && (0 == time)) { |
| return PICODATA_ITEMINFO1_BOUND_PHR0; |
| } |
| case PICODATA_ITEMINFO1_BOUND_PHR1: |
| case PICODATA_ITEMINFO1_BOUND_PHR2: |
| return modified; |
| break; |
| default: |
| return orig; |
| break; |
| } |
| } |
| |
| static picoos_bool breakStateInterrupting(picodata_itemhead_t * head, |
| picoos_bool * breakBefore, picoos_bool * breakAfter) { |
| |
| picoos_bool result = 1; |
| |
| *breakBefore = 0; |
| *breakAfter = 0; |
| |
| if (PICODATA_ITEM_WORDPHON == head->type) { |
| |
| } else if (PICODATA_ITEM_CMD == head->type) { |
| if ((PICODATA_ITEMINFO1_CMD_PLAY == head->info1) |
| || (PICODATA_ITEMINFO1_CMD_SAVE == head->info1) |
| || (PICODATA_ITEMINFO1_CMD_UNSAVE == head->info1)) { |
| *breakBefore = 1; |
| *breakAfter = 1; |
| } else if (PICODATA_ITEMINFO1_CMD_SAVE == head->info1) { |
| *breakBefore = 1; |
| } else if (PICODATA_ITEMINFO1_CMD_UNSAVE == head->info1) { |
| *breakAfter = 1; |
| } else if (PICODATA_ITEMINFO1_CMD_IGNSIG == head->info1) { |
| if (PICODATA_ITEMINFO2_CMD_START == head->info2) { |
| *breakBefore = 1; |
| } else { |
| *breakAfter = 1; |
| } |
| } |
| } else { |
| result = 0; |
| } |
| return result; |
| } |
| |
| |
| static void putSideBoundToOutput(spho_subobj_t * spho) |
| { |
| |
| picodata_itemhead_t ohead; |
| picoos_uint8 ocontent[2*sizeof(picoos_uint16)]; |
| picoos_int16 sildur; |
| picoos_uint16 clen; |
| |
| /* create boundary */ |
| ohead.type = PICODATA_ITEM_BOUND; |
| ohead.info1 = spho->headx[spho->outReadPos].boundstrength; |
| ohead.info2 = spho->headx[spho->outReadPos].phrasetype; |
| sildur = spho->headx[spho->outReadPos].sildur; |
| if ((sildur < 0) |
| || (PICODATA_ITEMINFO1_BOUND_PHR0 == ohead.info1) |
| || (PICODATA_ITEMINFO1_BOUND_PHR3 == ohead.info1)) { |
| PICODBG_DEBUG(("outputting a bound of strength '%c' and type '%c' without duration constraints",ohead.info1, ohead.info2)); |
| ohead.len = 0; |
| } else { |
| picoos_uint32 pos = 0; |
| picoos_write_mem_pi_uint16(ocontent,&pos,sildur); |
| picoos_write_mem_pi_uint16(ocontent,&pos,sildur); |
| PICODBG_DEBUG(("outputting a bound of strength '%c' and type '%c' with duration constraints [%i,%i]",ohead.info1, ohead.info2,sildur, sildur)); |
| ohead.len = pos; |
| } |
| picodata_put_itemparts(&ohead, ocontent, ohead.len, |
| spho->outBuf, spho->outBufSize, &clen); |
| /* disable side bound */ |
| spho->headx[spho->outReadPos].boundstrength = 0; |
| } |
| |
| /** Set bound strength and sil dur. |
| * |
| * given the original bound strength 'orig_strength' and the fst-suggested bound strength 'fst_strength' |
| * and possibly being in a pending break state, calculate the resulting bound strength and set boundstrength |
| * and sildur of the current item (spho->headx[spho->outReadPos]) accordingly. |
| * if a boundstrength was set, also calculate the phrasetype and if necessary (and reachable), modify the phrase type |
| * of the previous phrase boundary. |
| * |
| * @param spho |
| * @param orig_strength |
| * @param orig_type |
| * @param fst_strength |
| */ |
| static void setSideBound(spho_subobj_t * spho, picoos_uint8 orig_strength, picoos_uint8 orig_type, picoos_uint8 fst_strength) { |
| picoos_uint8 strength; |
| |
| /* insert modified bound according to transduction symbol, if any */ |
| if (PICODATA_ITEMINFO1_NA == orig_strength) { |
| /* no original/fst strength given */ |
| orig_strength = PICODATA_ITEMINFO1_BOUND_PHR0; |
| strength = PICODATA_ITEMINFO1_BOUND_PHR0; |
| } else { |
| strength = fstModifiedBoundStrength(orig_strength,fst_strength); |
| spho->headx[spho->outReadPos].boundstrength = strength; |
| spho->headx[spho->outReadPos].sildur = -1; |
| PICODBG_DEBUG(("setting bound strength to fst-suggested value %c (was %c)",strength, spho->headx[spho->outReadPos].boundstrength, spho->breakTime)); |
| } |
| |
| /* insert modified bound according to pending break, if any */ |
| if (spho->breakPending) { |
| /* the calculation is based on the fst-modified value (because this is what the customer wants to |
| * override) |
| */ |
| strength = breakModifiedBoundStrength(strength, spho->breakTime, (PICODATA_ITEMINFO1_BOUND_PHR1 == orig_strength)); |
| PICODBG_DEBUG(("setting bound strength to break-imposed value %c (was %c) and time to %i",strength, spho->headx[spho->outReadPos].boundstrength, spho->breakTime)); |
| spho->headx[spho->outReadPos].boundstrength = strength; |
| spho->headx[spho->outReadPos].sildur = spho->breakTime; |
| spho->breakPending = FALSE; |
| } |
| if (spho->headx[spho->outReadPos].boundstrength) { |
| /* we did set a bound strength, possibly promoting or demoting a boundary; now set the phrase type |
| * possibly also changing the phrase type of the previous phrase bound |
| */ |
| picoos_uint8 fromPhrase = ((PICODATA_ITEMINFO1_BOUND_PHR0 != orig_strength)); |
| picoos_uint8 toPhrase = ((PICODATA_ITEMINFO1_BOUND_PHR0 != strength)); |
| |
| PICODBG_DEBUG(("setting phrase type (wasPhrase=%i, isPhrase=%i)",fromPhrase,toPhrase)); |
| if (toPhrase) { |
| if (fromPhrase) { |
| spho->lastPhraseType = orig_type; |
| } else { /*promote */ |
| if (spho->activeStartPos <= spho->lastPhraseBoundPos) { |
| /* we still can change prev phrase bound */ |
| /* since a new phrase boundary is introduced, we have to 'invent' |
| * an additional phrase type here. For that, we have to use some of the |
| * knowledge that otherwise is handled in picoacph. |
| */ |
| spho->headx[spho->lastPhraseBoundPos].phrasetype |
| = PICODATA_ITEMINFO2_BOUNDTYPE_P; |
| } |
| } |
| spho->lastPhraseBoundPos = spho->outReadPos; |
| spho->headx[spho->lastPhraseBoundPos].phrasetype |
| = spho->lastPhraseType; |
| |
| } else { |
| spho->headx[spho->outReadPos].phrasetype = PICODATA_ITEMINFO2_NA; |
| if (fromPhrase) { /* demote */ |
| spho->lastPhraseType = orig_type; |
| if (spho->activeStartPos <= spho->lastPhraseBoundPos) { |
| /* we still can change prev phrase bound */ |
| spho->headx[spho->lastPhraseBoundPos].phrasetype |
| = spho->lastPhraseType; |
| } |
| } |
| } |
| } |
| } |
| |
| |
| /* ***********************************************************************/ |
| /* sphoStep function */ |
| /* ***********************************************************************/ |
| |
| |
| static picodata_step_result_t sphoStep(register picodata_ProcessingUnit this, |
| picoos_int16 mode, picoos_uint16 * numBytesOutput) |
| { |
| |
| register spho_subobj_t *spho; |
| pico_status_t rv= PICO_OK; |
| picoos_uint16 blen; |
| picodata_itemhead_t ihead, ohead; |
| picoos_uint8 *icontent; |
| picoos_uint16 nextInPos; |
| #if defined(PICO_DEBUG) |
| picoos_char msgstr[SPHO_MSGSTR_SIZE]; |
| #endif |
| |
| /* used in FEED and FEED_SYM */ |
| picoos_uint16 clen; |
| picoos_int16 pos, sym, sylsym; |
| picoos_uint8 plane; |
| |
| /* used in BOUNDS */ |
| picoos_bool breakBefore, breakAfter; |
| |
| /* pico_status_t rvP= PICO_OK; */ |
| |
| picoos_uint16 curPos /*, nextPos */; |
| picoos_uint16 remHeadxSize, remCbufSize; |
| |
| |
| if (NULL == this || NULL == this->subObj) { |
| return PICODATA_PU_ERROR; |
| } |
| spho = (spho_subobj_t *) this->subObj; |
| |
| mode = mode; /* avoid warning "var not used in this function"*/ |
| |
| *numBytesOutput = 0; |
| while (1) { /* exit via return */ |
| PICODBG_INFO(("doing state %i, headxReadPos: %d, headxWritePos: %d", |
| spho->procState, spho->headxReadPos, spho->headxWritePos)); |
| |
| switch (spho->procState) { |
| |
| case SPHO_STEPSTATE_INIT: |
| /* **********************************************************************/ |
| /* INIT */ |
| /* **********************************************************************/ |
| PICODBG_DEBUG(("INIT")); |
| /* (re)set values for PARSE */ |
| spho->penultima = SPHO_POS_INVALID; |
| spho->activeEndPos = SPHO_POS_INVALID; |
| spho->headxReadPos = 0; |
| spho->phonReadPos = 0; |
| spho->phonWritePos = 0; |
| spho->lastPhraseType = PICODATA_ITEMINFO2_NA; |
| spho->lastPhraseBoundPos = -1; |
| |
| spho->procState = SPHO_STEPSTATE_COLLECT; |
| break; |
| |
| |
| case SPHO_STEPSTATE_COLLECT: |
| /* **********************************************************************/ |
| /* COLLECT */ |
| /* **********************************************************************/ |
| /* collect state: get items from charBuf and store in |
| * internal inBuf |
| */ |
| PICODBG_TRACE(("COLLECT")); |
| rv = PICO_OK; |
| remHeadxSize = spho->headxBufSize - spho->headxWritePos; |
| remCbufSize = spho->cbufBufSize - spho->cbufWritePos; |
| curPos = spho->headxWritePos; |
| while ((PICO_OK == rv) && (remHeadxSize > 0) && (remCbufSize > 0)) { |
| PICODBG_DEBUG(("COLLECT getting item at headxWritePos %i (remaining %i)",spho->headxWritePos, remHeadxSize)); |
| rv = picodata_cbGetItem(this->cbIn, spho->tmpbuf, PICODATA_MAX_ITEMSIZE, &blen); |
| if (PICO_OK == rv) { |
| rv = picodata_get_itemparts(spho->tmpbuf, |
| PICODATA_MAX_ITEMSIZE, &(spho->headx[spho->headxWritePos].head), |
| &(spho->cbuf[spho->cbufWritePos]), remCbufSize, &blen); |
| if (PICO_OK == rv) { |
| spho->headx[spho->headxWritePos].cind = spho->cbufWritePos; |
| spho->headx[spho->headxWritePos].boundstrength = 0; |
| spho->headxWritePos++; |
| remHeadxSize--; |
| spho->cbufWritePos += blen; |
| remCbufSize -= blen; |
| } |
| } |
| } |
| if ((PICO_OK == rv) && ((remHeadxSize <= 0) || (remCbufSize <= 0))) { |
| rv = PICO_EXC_BUF_OVERFLOW; |
| } |
| |
| /* in normal circumstances, rv is either PICO_EOF (no more items in cbIn) or PICO_BUF_OVERFLOW |
| * (if no more items fit into headx) */ |
| if ((PICO_EOF != rv) && (PICO_EXC_BUF_OVERFLOW != rv)) { |
| PICODBG_DEBUG(("COLLECT ** problem getting item, unhandled, rv: %i", rv)); |
| picoos_emRaiseException(this->common->em, rv, |
| NULL, NULL); |
| return PICODATA_PU_ERROR; |
| } |
| if (PICO_EOF == rv) { /* there are no more items available */ |
| if (curPos < spho->headxWritePos) { /* we did get some new items */ |
| PICODBG_DEBUG(("COLLECT read %i items", |
| spho->headxWritePos - curPos)); |
| spho->needMoreInput = FALSE; |
| } |
| if (spho->needMoreInput) { /* not enough items to proceed */ |
| PICODBG_DEBUG(("COLLECT need more data, returning IDLE")); |
| return PICODATA_PU_IDLE; |
| } else { |
| spho->procState = SPHO_STEPSTATE_PROCESS_PARSE; |
| /* uncomment next to split into two steps */ |
| /* return PICODATA_PU_ATOMIC; */ |
| } |
| } else { /* input buffer full */ |
| PICODBG_DEBUG(("COLLECT input buffer full")); |
| if (spho->needMoreInput) { /* forced output because we can't get more data */ |
| spho->needMoreInput = FALSE; |
| spho->force = TRUE; |
| } |
| spho->procState = SPHO_STEPSTATE_PROCESS_PARSE; |
| } |
| break; |
| |
| case SPHO_STEPSTATE_PROCESS_PARSE: |
| |
| /* **********************************************************************/ |
| /* PARSE: items -> input pos/phon pairs */ |
| /* **********************************************************************/ |
| |
| /* parse one item at a time */ |
| /* If |
| * - the item is a sentence end or |
| * - it is the last item and force=1 or |
| * - the phon buffer is full |
| * then set inReadPos to 0 and go to TRANSDUCE |
| * else advance by one item */ |
| |
| /* look at the current item */ |
| PICODBG_TRACE(("PARSE")); |
| if (spho->headxReadPos >= spho->headxWritePos) { |
| /* no more items in headx */ |
| if (spho->force) { |
| PICODBG_INFO(("no more items in headx but we are forced to transduce")); |
| |
| /* headx is full; we are forced to transduce before reaching the sentence end */ |
| spho->force = FALSE; |
| if (SPHO_POS_INVALID == spho->activeEndPos) { |
| spho->activeEndPos = spho->headxReadPos; |
| } |
| spho->procState = SPHO_STEPSTATE_PROCESS_TRANSDUCE; |
| } else { |
| /* we try to get more data */ |
| PICODBG_INFO(("no more items in headx, try to collect more")); |
| spho->needMoreInput = TRUE; |
| spho->procState = SPHO_STEPSTATE_COLLECT; |
| } |
| break; |
| } |
| |
| ihead = spho->headx[spho->headxReadPos].head; |
| icontent = spho->cbuf + spho->headx[spho->headxReadPos].cind; |
| |
| PICODBG_DEBUG(("PARSE looking at item %s",picodata_head_to_string(&ihead,msgstr,SPHO_MSGSTR_SIZE))); |
| /* treat header */ |
| if (PICODATA_ITEM_BOUND == ihead.type) { |
| /* see if it is a sentence end or termination boundary (flush) */ |
| if ((PICODATA_ITEMINFO1_BOUND_SEND == ihead.info1) |
| || (PICODATA_ITEMINFO1_BOUND_TERM == ihead.info1)) { |
| PICODBG_INFO(("PARSE found sentence end or term BOUND")); |
| |
| if (spho->sentenceStarted) { |
| /* its the end of the sentence */ |
| PICODBG_INFO(("PARSE found sentence end")); |
| spho->sentenceStarted = 0; |
| /* there is no need for a right context; move the active end to the end */ |
| /* add sentence termination phonemes */ |
| sphoAddTermPhonemes(spho, spho->headxReadPos); |
| spho->headxReadPos++; |
| spho->activeEndPos = spho->headxReadPos; |
| /* we may discard all information up to activeEndPos, after processing of last |
| * sentence part |
| */ |
| spho->penultima = spho->activeEndPos; |
| |
| /* transduce */ |
| spho->procState = SPHO_STEPSTATE_PROCESS_TRANSDUCE; |
| /* uncomment to split */ |
| /* return PICODATA_PU_BUSY; */ |
| break; |
| } else { |
| if (PICODATA_ITEMINFO1_BOUND_TERM == ihead.info1) { |
| /* its the end of input (flush) */ |
| PICODBG_INFO(("PARSE forwarding input end (flush)")); |
| /* copy item unmodified */ |
| picodata_put_itemparts(&ihead, |
| icontent, |
| ihead.len, |
| spho->outBuf, spho->outBufSize, |
| &clen); |
| |
| spho->headxReadPos++; |
| spho->activeEndPos = spho->headxReadPos; |
| spho->penultima = SPHO_POS_INVALID; |
| spho->feedFollowState = SPHO_STEPSTATE_SHIFT; |
| spho->procState = SPHO_STEPSTATE_FEED; |
| break; |
| } else { |
| /* this should never happen */ |
| /* eliminate bound */ |
| spho->headxReadPos++; |
| spho->activeEndPos = spho->headxReadPos; |
| spho->penultima = SPHO_POS_INVALID; |
| PICODBG_ERROR(("PARSE found a sentence end without a sentence start; eliminated")); |
| } |
| } |
| } else if (PICODATA_ITEMINFO1_BOUND_SBEG == ihead.info1) { |
| /* its the start of the sentence */ |
| PICODBG_INFO(("PARSE found sentence start")); |
| /* add sentence starting phoneme */ |
| sphoAddStartPhoneme(spho); |
| |
| spho->sentenceStarted = 1; |
| } |
| } |
| |
| if ((PICODATA_ITEM_WORDPHON == ihead.type) |
| || (PICODATA_ITEM_BOUND == ihead.type)) { |
| /* if it is a word or a bound try to extract phonemes */ |
| PICODBG_INFO(("PARSE found WORD phon or phrase BOUND")); |
| rv = sphoExtractPhonemes(this, spho, spho->headxReadPos, |
| TRUE /* convertAccents */, |
| &spho->suppressParseWordBound); |
| if (PICO_OK == rv) { |
| PICODBG_INFO(("PARSE successfully returned from phoneme extraction")); |
| /* replace activeEndPos if the new item is a word, or activeEndPos was not set yet, or |
| * activeEndPos was a bound */ |
| if ((spho->activeStartPos <= spho->headxReadPos) && ((PICODATA_ITEM_WORDPHON == ihead.type) |
| || (SPHO_POS_INVALID == spho->activeEndPos) |
| || (PICODATA_ITEM_BOUND == spho->headx[spho->activeEndPos].head.type))) { |
| PICODBG_INFO(("PARSE found new activeEndPos: %i,%i -> %i,%i", |
| spho->penultima,spho->activeEndPos,spho->activeEndPos,spho->headxReadPos)); |
| spho->penultima = spho->activeEndPos; |
| spho->activeEndPos = spho->headxReadPos; |
| } |
| |
| } else if (PICO_EXC_BUF_OVERFLOW == rv) { |
| /* phoneme buffer cannot take this item anymore; |
| if the phoneme buffer has some contents, we are forced to transduce before reaching the sentence end |
| else we skip the (too long word) */ |
| PICODBG_INFO(("PARSE returned from phoneme extraction with overflow, number of phonemes in phonBuf: %i; forced to TRANSDUCE", spho->phonWritePos)); |
| if ((SPHO_POS_INVALID == spho->activeEndPos) || (spho->activeStartPos == spho->activeEndPos)) { |
| spho->activeEndPos = spho->headxReadPos; |
| } |
| spho->procState = SPHO_STEPSTATE_PROCESS_TRANSDUCE; |
| break; |
| } else { |
| PICODBG_ERROR(("PARSE returned from phoneme extraction with exception %i",rv)); |
| return (picodata_step_result_t)picoos_emRaiseException(this->common->em, |
| PICO_ERR_OTHER, NULL, NULL); |
| } |
| } else { |
| PICODBG_INFO(("PARSE found other item, passing over")); |
| /* it is "other" item, ignore */ |
| } |
| /* set pos at next item */ |
| PICODBG_INFO(("PARSE going to next item: %i -> %i",spho->headxReadPos, spho->headxReadPos + 1)); |
| spho->headxReadPos++; |
| break; |
| |
| case SPHO_STEPSTATE_PROCESS_TRANSDUCE: |
| |
| /* **********************************************************************/ |
| /* TRANSDUCE: transduction input pos/phon pairs to output pos/phon pairs */ |
| /* **********************************************************************/ |
| PICODBG_DEBUG(("TRANSDUCE (%i-th of %i fsts",spho->curFst+1, spho->numFsts)); |
| |
| /* termination condition first */ |
| if (spho->curFst >= spho->numFsts) { |
| |
| #if defined(PICO_DEBUG) |
| { |
| PICODBG_INFO_CTX(); |
| PICODBG_INFO_MSG(("result of all transductions: ")); |
| PICOTRNS_PRINTSYMSEQ(this->voice->kbArray[PICOKNOW_KBID_DBG], spho->phonBufOut, spho->phonWritePos); |
| PICODBG_INFO_MSG(("\n")); |
| } |
| #endif |
| |
| /* reset for next transduction */ |
| spho->curFst = 0; |
| /* prepare BOUNDS */ |
| spho->outReadPos = 0; |
| spho->phonReadPos = 0; |
| |
| spho->procState = SPHO_STEPSTATE_PROCESS_BOUNDS; |
| break; |
| } |
| |
| /* transduce from phonBufIn to PhonBufOut */ |
| { |
| |
| picoos_uint32 nrSteps; |
| #if defined(PICO_DEBUG) |
| { |
| PICODBG_INFO_CTX(); |
| PICODBG_INFO_MSG(("spho trying to transduce: ")); |
| PICOTRNS_PRINTSYMSEQ(this->voice->kbArray[PICOKNOW_KBID_DBG], spho->phonBuf, spho->phonWritePos); |
| PICODBG_INFO_MSG(("\n")); |
| } |
| #endif |
| rv = picotrns_transduce(spho->fst[spho->curFst], FALSE, |
| picotrns_printSolution, spho->phonBuf, spho->phonWritePos, spho->phonBufOut, |
| &spho->phonWritePos, |
| 4*PICOTRNS_MAX_NUM_POSSYM, spho->altDescBuf, |
| spho->maxAltDescLen, &nrSteps); |
| if (PICO_OK == rv) { |
| #if defined(PICO_DEBUG) |
| { |
| PICODBG_INFO_CTX(); |
| PICODBG_INFO_MSG(("result of transduction: (output symbols: %i)", spho->phonWritePos)); |
| PICOTRNS_PRINTSYMSEQ(this->voice->kbArray[PICOKNOW_KBID_DBG], spho->phonBufOut, spho->phonWritePos); |
| PICODBG_INFO_MSG(("\n")); |
| } |
| #endif |
| PICODBG_TRACE(("number of steps done in tranduction: %i", nrSteps)); |
| } else { |
| picoos_emRaiseWarning(this->common->em, PICO_WARN_FALLBACK,NULL,(picoos_char *)"phon buffer full"); |
| } |
| } |
| /* eliminate deep epsilons */ |
| picotrns_eliminate_epsilons(spho->phonBufOut, spho->phonWritePos, spho->phonBuf, |
| &spho->phonWritePos,4*PICOTRNS_MAX_NUM_POSSYM); |
| |
| spho->curFst++; |
| |
| /* return PICODATA_PU_ATOMIC */ |
| break; |
| |
| |
| case SPHO_STEPSTATE_PROCESS_BOUNDS: |
| /* ************************************************************************/ |
| /* BOUNDS: combine input item with pos/phon pairs to insert/modify bounds */ |
| /* ************************************************************************/ |
| |
| PICODBG_INFO(("BOUNDS")); |
| |
| /* get the suppressRecombWordBound in the left context */ |
| spho->suppressRecombWordBound = FALSE; |
| while (spho->outReadPos < spho->activeStartPos) { |
| /* look at the current item */ |
| ihead = spho->headx[spho->outReadPos].head; |
| /* icontent = spho->cbuf + spho->headx[spho->outReadPos].cind; */ |
| PICODBG_INFO(("in position %i, looking at item %s",spho->outReadPos,picodata_head_to_string(&ihead,msgstr,SPHO_MSGSTR_SIZE))); |
| if (PICODATA_ITEM_BOUND == ihead.type) { |
| spho->suppressRecombWordBound = TRUE; |
| } else if (PICODATA_ITEM_WORDPHON == ihead.type) { |
| spho->suppressRecombWordBound = FALSE; |
| } |
| spho->outReadPos++; |
| } |
| /* spho->outReadPos point now to the active region */ |
| |
| /* advance the phone reading pos to the active range */ |
| spho->phonReadPos = 0; |
| while (SPHO_POSSYM_OK == (rv = getNextPosSym(spho, &pos, &sym, |
| spho->activeStartPos))) { |
| /* ignore */ |
| } |
| PICODBG_INFO(("skipping left context phones results in %s", (SPHO_POSSYM_OUT_OF_RANGE==rv) ? "OUT_OF_RANGE" : (SPHO_POSSYM_END ==rv) ? "END" : "OTHER")); |
| |
| /* |
| * Align input items with transduced phones and note bound stregth changes and break commands |
| */ |
| |
| while (spho->outReadPos < spho->activeEndPos) { |
| |
| /* look at the current item */ |
| ihead = spho->headx[spho->outReadPos].head; |
| icontent = spho->cbuf + spho->headx[spho->outReadPos].cind; |
| nextInPos = spho->outReadPos + 1; |
| /* */ |
| PICODBG_INFO(("in position %i, looking at item %s",spho->outReadPos,picodata_head_to_string(&ihead,msgstr,SPHO_MSGSTR_SIZE))); |
| |
| if ((PICODATA_ITEM_BOUND == ihead.type) |
| || ((PICODATA_ITEM_WORDPHON == ihead.type) |
| && (!spho->suppressRecombWordBound))) { |
| /* there was a boundary originally */ |
| picoos_uint8 orig_strength, orig_type; |
| if (PICODATA_ITEM_BOUND == ihead.type) { |
| orig_strength = ihead.info1; |
| orig_type = ihead.info2; |
| spho->suppressRecombWordBound = TRUE; |
| } else { |
| orig_strength = PICODATA_ITEMINFO1_BOUND_PHR0; |
| orig_type = PICODATA_ITEMINFO2_NA; |
| } |
| /* i expect a boundary phone here */ |
| /* consume FST bound phones, consider pending break and set the side-bound */ |
| PICODBG_INFO(("got BOUND or WORDPHON item and expects corresponding phone")); |
| rv = getNextPosSym(spho, &pos, &sym, nextInPos); |
| if (SPHO_POSSYM_OK != rv) { |
| 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")); |
| return (picodata_step_result_t)picoos_emRaiseException(this->common->em, |
| PICO_ERR_OTHER, NULL, NULL); |
| } |
| sym = picotrns_unplane(sym, &plane); |
| /* */ |
| PICODBG_ASSERT((PICOKFST_PLANE_PB_STRENGTHS == plane)); |
| |
| /* insert modified bound according to transduction and possibly pending break */ |
| setSideBound(spho, orig_strength, orig_type, |
| (picoos_uint8) sym); |
| } else if ((PICODATA_ITEM_CMD == ihead.type) |
| && (PICODATA_ITEMINFO1_CMD_SIL == ihead.info1)) { |
| /* it's a SIL (break) command */ |
| picoos_uint16 time; |
| picoos_uint32 pos = 0; |
| picoos_read_mem_pi_uint16(icontent, &pos, &time); |
| if (spho->breakPending) { |
| spho->breakTime += time; |
| } else { |
| spho->breakTime = time; |
| spho->breakPending = TRUE; |
| } |
| } else if ((PICODATA_ITEM_CMD == ihead.type) && (PICODATA_ITEMINFO1_CMD_PLAY == ihead.info1)) { |
| /* insert break of at least one ms */ |
| if (!spho->breakPending || (spho->breakTime <= 0)) { |
| spho->breakTime = SPHO_SMALLEST_SIL_DUR; |
| spho->breakPending = TRUE; |
| } |
| setSideBound(spho, PICODATA_ITEMINFO1_NA, |
| PICODATA_ITEMINFO2_NA, PICODATA_ITEMINFO1_NA); |
| /* force following break to be at least one ms */ |
| spho->breakTime = SPHO_SMALLEST_SIL_DUR; |
| spho->breakPending = TRUE; |
| } else if (breakStateInterrupting(&ihead, &breakBefore, &breakAfter)) { |
| |
| if (breakBefore &&(!spho->breakPending || (spho->breakTime <= 0))) { |
| spho->breakTime = SPHO_SMALLEST_SIL_DUR; |
| spho->breakPending = TRUE; |
| } |
| setSideBound(spho, PICODATA_ITEMINFO1_NA, |
| PICODATA_ITEMINFO2_NA, PICODATA_ITEMINFO1_NA); |
| |
| if (breakAfter) { |
| spho->breakTime = SPHO_SMALLEST_SIL_DUR; |
| spho->breakPending = TRUE; |
| } |
| if (PICODATA_ITEM_WORDPHON == ihead.type) { |
| spho->suppressRecombWordBound = FALSE; |
| } |
| } |
| |
| /* skip phones of that item */ |
| while (SPHO_POSSYM_OK == (rv = getNextPosSym(spho, &pos, |
| &sym, nextInPos))) { |
| /* ignore */ |
| } |
| spho->outReadPos++; |
| } |
| |
| /* reset for RECOMB */ |
| spho->outReadPos = 0; |
| spho->phonReadPos = 0; |
| spho->suppressRecombWordBound = FALSE; |
| |
| spho->procState = SPHO_STEPSTATE_PROCESS_RECOMB; |
| return PICODATA_PU_ATOMIC; |
| |
| break; |
| |
| case SPHO_STEPSTATE_PROCESS_RECOMB: |
| /* **********************************************************************/ |
| /* RECOMB: combine input item with pos/phon pairs to output item */ |
| /* **********************************************************************/ |
| |
| PICODBG_TRACE(("RECOMB")); |
| |
| /* default place to come after feed: here */ |
| spho->feedFollowState = SPHO_STEPSTATE_PROCESS_RECOMB; |
| |
| /* check termination condition first */ |
| if (spho->outReadPos >= spho->activeEndPos) { |
| PICODBG_DEBUG(("RECOMB reached active region's end at %i",spho->outReadPos)); |
| spho->procState = SPHO_STEPSTATE_SHIFT; |
| break; |
| } |
| |
| /* look at the current item */ |
| ihead = spho->headx[spho->outReadPos].head; |
| icontent = spho->cbuf + spho->headx[spho->outReadPos].cind; |
| |
| PICODBG_DEBUG(("RECOMB looking at item %s",picodata_head_to_string(&ihead,msgstr,SPHO_MSGSTR_SIZE))); |
| |
| nextInPos = spho->outReadPos + 1; |
| |
| PICODBG_DEBUG(("RECOMB treating item in headx at pos %i",spho->outReadPos)); |
| if (nextInPos <= spho->activeStartPos) { /* we're in the (passive) left context. Just skip it */ |
| PICODBG_DEBUG(("RECOMB skipping item in the left context (%i <= %i)",nextInPos, spho->activeStartPos)); |
| if (PICODATA_ITEM_BOUND == ihead.type) { |
| spho->suppressRecombWordBound = 1; |
| } else if (PICODATA_ITEM_WORDPHON == ihead.type) { |
| spho->suppressRecombWordBound = 0; |
| } |
| |
| /* consume possyms */ |
| while (SPHO_POSSYM_OK == (rv = getNextPosSym(spho,&pos,&sym,nextInPos))) { |
| /* ignore */ |
| } |
| if (rv == SPHO_POSSYM_INVALID) { |
| return (picodata_step_result_t)picoos_emRaiseException(this->common->em, |
| PICO_ERR_OTHER, NULL, NULL); |
| } |
| spho->outReadPos = nextInPos; |
| } else { /* active region */ |
| if (spho->headx[spho->outReadPos].boundstrength) { |
| /* ***************** "side-bound" *********************/ |
| /* copy to outbuf */ |
| putSideBoundToOutput(spho); |
| /* mark as processed */ |
| spho->headx[spho->outReadPos].boundstrength = 0; |
| /* output it */ |
| spho->procState = SPHO_STEPSTATE_FEED; |
| } else if (PICODATA_ITEM_BOUND == ihead.type) { |
| /* ***************** BOUND *********************/ |
| /* expect a boundary phone here */ |
| PICODBG_DEBUG(("RECOMB got BOUND item and expects corresponding phone")); |
| rv = getNextPosSym(spho, &pos, &sym, nextInPos); |
| if (SPHO_POSSYM_OK != rv) { |
| PICODBG_ERROR(("unexpected symbol or unexpected end of phoneme list")); |
| return (picodata_step_result_t)picoos_emRaiseException( |
| this->common->em, PICO_ERR_OTHER, NULL, |
| NULL); |
| } |
| sym = picotrns_unplane(sym, &plane); |
| /* */ |
| PICODBG_ASSERT((PICOKFST_PLANE_PB_STRENGTHS == plane)); |
| |
| spho->suppressRecombWordBound = TRUE; /* if word following, don't need word boundary */ |
| /* just consume item and come back here*/ |
| spho->outReadPos = nextInPos; |
| |
| } else if (PICODATA_ITEM_WORDPHON == ihead.type) { |
| /* ***************** WORDPHON *********************/ |
| spho->wordStarted = TRUE; |
| /* i expect a word boundary symbol in this range unless a phrase boundary was encountered before */ |
| if (spho->suppressRecombWordBound) { |
| PICODBG_DEBUG(("RECOMB got WORDPHON item but skips expecting BOUND")); |
| spho->suppressRecombWordBound = FALSE; |
| } else { |
| PICODBG_DEBUG(("RECOMB got WORDPHON item and expects corresponding bound phone")); |
| rv = getNextPosSym(spho, &pos, &sym, nextInPos); |
| if (SPHO_POSSYM_OK != rv) { |
| PICODBG_ERROR(("unexpected symbol or unexpected end of phoneme list")); |
| return (picodata_step_result_t)picoos_emRaiseException(this->common->em, |
| PICO_ERR_OTHER, NULL, NULL); |
| } |
| } |
| spho->procState = SPHO_STEPSTATE_PROCESS_SYL; |
| } else if ((PICODATA_ITEM_CMD == ihead.type) && (PICODATA_ITEMINFO1_CMD_SIL == ihead.info1)) { |
| /* ***************** BREAK COMMAND *********************/ |
| /* just consume and come back here */ |
| PICODBG_DEBUG(("RECOMB consuming item from inBuf %i -> %i",spho->outReadPos, nextInPos)); |
| spho->outReadPos = nextInPos; |
| } else { |
| /* ***************** OTHER *********************/ |
| /* just copy item */ |
| PICODBG_DEBUG(("RECOMB found other item, just copying")); |
| picodata_put_itemparts(&ihead, icontent, ihead.len, |
| spho->outBuf, spho->outBufSize, &clen); |
| PICODBG_DEBUG(("RECOMB consuming item from inBuf %i -> %i",spho->outReadPos, nextInPos)); |
| spho->outReadPos = nextInPos; |
| /* and output it */ |
| spho->procState = SPHO_STEPSTATE_FEED; |
| } /* if (ihead.type) */ |
| |
| } |
| |
| /* return PICODATA_PU_BUSY; */ |
| break; |
| |
| case SPHO_STEPSTATE_PROCESS_SYL: |
| /* **********************************************************************/ |
| /* SYL: combine input word item with pos/phon pairs to syl output item */ |
| /* **********************************************************************/ |
| |
| /* consume all transduced phonemes with pos in in the range [spho->outReadPos,nextInPos[ */ |
| PICODBG_DEBUG(("SYL")); |
| |
| spho->feedFollowState = SPHO_STEPSTATE_PROCESS_SYL; |
| |
| /* look at the current item */ |
| ihead = spho->headx[spho->outReadPos].head; |
| icontent = spho->cbuf + spho->headx[spho->outReadPos].cind; |
| nextInPos = spho->outReadPos + 1; |
| PICODBG_DEBUG(("SYL (1) treating item in headx at pos %i",spho->outReadPos)); |
| /* create syllable item in ohead (head) and sylBuf (contents) */ |
| ohead.type = PICODATA_ITEM_SYLLPHON; |
| |
| 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)); |
| rv = getNextPosSym(spho,&pos,&sym,nextInPos); |
| if (SPHO_POSSYM_OK != rv) { |
| PICODBG_ERROR(("unexpected symbol or unexpected end of phoneme list (%i)",rv)); |
| return (picodata_step_result_t)picoos_emRaiseException(this->common->em, PICO_ERR_OTHER, NULL, NULL); |
| } |
| ohead.info2 = picotrns_unplane(sym, &plane); |
| PICODBG_ASSERT((PICOKFST_PLANE_ACCENTS == plane)); |
| PICODBG_DEBUG(("SYL sets accent to %c", sym)); |
| |
| /* for the time being, we force to use POS so we can transduce all fsts in a row without reconsulting the items */ |
| PICODBG_TRACE(("SYL expects POS")); |
| PICODBG_DEBUG(("SYL (2) treating item in inBuf range [%i,%i[",spho->outReadPos,nextInPos)); |
| rv = getNextPosSym(spho,&pos,&sym,nextInPos); |
| if (SPHO_POSSYM_OK != rv) { |
| PICODBG_ERROR(("unexpected symbol or unexpected end of phoneme list")); |
| return (picodata_step_result_t)picoos_emRaiseException(this->common->em, PICO_ERR_OTHER, NULL, NULL); |
| } |
| if (spho->wordStarted) { |
| spho->wordStarted = FALSE; |
| ohead.info1 = picotrns_unplane(sym, &plane); |
| /* */ |
| PICODBG_ASSERT(PICOKFST_PLANE_POS == plane); |
| /* */ |
| PICODBG_DEBUG(("SYL setting POS to %c", ohead.info1)); |
| } else { |
| ohead.info1 = PICODATA_ITEMINFO1_NA; |
| } |
| |
| PICODBG_DEBUG(("SYL (3) treating item in inBuf range [%i,%i[",spho->outReadPos,nextInPos)); |
| /* get phonemes of that syllable; stop if syllable boundary or outside word */ |
| sylsym = (PICOKFST_PLANE_PHONEMES << 8) |
| + spho->syllSepId; |
| PICODBG_DEBUG(("collecting syllable phonemes before headx position %i",nextInPos)); |
| spho->sylWritePos = 0; |
| while (SPHO_POSSYM_OK == (rv = getNextPosSym(spho,&pos,&sym,nextInPos)) && (sym != sylsym)) { |
| spho->sylBuf[spho->sylWritePos++] = picotrns_unplane(sym, &plane); |
| /* */ |
| PICODBG_TRACE(("SYL adding phoneme to syllable: (pos %i,sym %i)[plane %i,sym %c]",pos,sym,plane,sym & 0xFF)); |
| PICODBG_ASSERT((PICOKFST_PLANE_PHONEMES == plane)); |
| } |
| PICODBG_DEBUG(("SYL (4) treating item in inBuf range [%i,%i[",spho->outReadPos,nextInPos)); |
| ohead.len = spho->sylWritePos; |
| if (SPHO_POS_INVALID == rv) { |
| PICODBG_ERROR(("unexpected symbol or unexpected end of phoneme list")); |
| return (picodata_step_result_t)picoos_emRaiseException(this->common->em, PICO_WARN_INCOMPLETE, NULL, NULL); |
| } else if ((SPHO_POSSYM_OUT_OF_RANGE == rv) || (SPHO_POSSYM_END == rv)) { |
| PICODBG_DEBUG(("SYL arrived at end of word and/or end of phon buffer, go to next word")); |
| spho->outReadPos = nextInPos; /* advance to next item */ |
| spho->feedFollowState = SPHO_STEPSTATE_PROCESS_RECOMB; /* go to RECOMB after feed */ |
| } else { |
| PICODBG_ASSERT((sym == sylsym)); |
| } |
| PICODBG_DEBUG(("SYL (5) treating item in inBuf range [%i,%i[",spho->outReadPos,nextInPos)); |
| |
| if (ohead.len > 0) { |
| /* prepare syllable output */ |
| picodata_put_itemparts(&ohead, spho->sylBuf, |
| PICODATA_BUFSIZE_DEFAULT, spho->outBuf, |
| spho->outBufSize, &clen); |
| |
| spho->procState = SPHO_STEPSTATE_FEED; |
| } else { /* skip feeding output of empty syllable */ |
| spho->procState = spho->feedFollowState; |
| } |
| break; |
| |
| case SPHO_STEPSTATE_FEED: |
| /* **********************************************************************/ |
| /* FEED: output output item and proceed to feedFollowState */ |
| /* **********************************************************************/ |
| |
| PICODBG_DEBUG(("FEED")); |
| |
| PICODBG_DEBUG(("FEED putting outBuf item into cb")); |
| |
| /*feeding items to PU output buffer*/ |
| rv = picodata_cbPutItem(this->cbOut, spho->outBuf, |
| spho->outBufSize, &clen); |
| |
| PICODATA_INFO_ITEM(this->voice->kbArray[PICOKNOW_KBID_DBG], |
| (picoos_uint8 *)"spho: ", |
| spho->outBuf, spho->outBufSize); |
| |
| if (PICO_EXC_BUF_OVERFLOW == rv) { |
| /* we have to redo this item */ |
| PICODBG_DEBUG(("FEED got overflow, returning ICODATA_PU_OUT_FULL")); |
| return PICODATA_PU_OUT_FULL; |
| } else if (PICO_OK == rv) { |
| *numBytesOutput += clen; |
| spho->procState = spho->feedFollowState; |
| PICODBG_DEBUG(("FEED ok, going back to procState %i", spho->procState)); |
| return PICODATA_PU_BUSY; |
| } else { |
| PICODBG_DEBUG(("FEED got exception %i when trying to output item",rv)); |
| spho->procState = spho->feedFollowState; |
| return (picodata_step_result_t)rv; |
| } |
| break; |
| |
| case SPHO_STEPSTATE_SHIFT: |
| /* **********************************************************************/ |
| /* SHIFT */ |
| /* **********************************************************************/ |
| /* If there exists a valid penultima, it should replace any left context (from 0 to activeStartPos) |
| * else discard the current active range (from activeStartPos to activeEndPos), leaving the current |
| * left context intact. Often, PARSE would move activeStartPos to 0, so that there is no left context |
| * after the shift. |
| */ |
| |
| PICODBG_DEBUG(("SHIFT")); |
| |
| if (spho->penultima != SPHO_POS_INVALID) { |
| picoos_int16 shift; |
| /* set penultima as new left context and set activeStartPos to the shifted activeEndPos */ |
| PICODBG_DEBUG(( |
| "SHIFT shifting penultima from %i to 0", |
| spho->penultima)); |
| shift = shift_range_left_1(spho, &spho->penultima, 0); |
| if (shift < 0) { |
| picoos_emRaiseException(this->common->em,PICO_ERR_OTHER,NULL,NULL); |
| return PICODATA_PU_ERROR; |
| } |
| spho->activeStartPos = spho->activeEndPos |
| - shift; |
| spho->lastPhraseBoundPos -= shift; |
| spho->suppressParseWordBound = FALSE; |
| spho->suppressRecombWordBound = FALSE; |
| |
| } else { |
| picoos_int16 shift; |
| picoos_bool lastPhraseBoundActive; |
| if (spho->activeStartPos == spho->activeEndPos) { |
| /* no items consumed; we have to abandon left context */ |
| spho->activeStartPos = 0; |
| } |
| lastPhraseBoundActive = (spho->lastPhraseBoundPos >= spho->activeStartPos); |
| /* dummy comment */ |
| PICODBG_DEBUG(("SHIFT shift active end from %i to %i", |
| spho->activeEndPos, spho->activeStartPos)); |
| shift = shift_range_left_1(spho, &spho->activeEndPos, spho->activeStartPos); |
| if (shift < 0) { |
| picoos_emRaiseException(this->common->em,PICO_ERR_OTHER,NULL,NULL); |
| return PICODATA_PU_ERROR; |
| } |
| if (lastPhraseBoundActive) { |
| spho->lastPhraseBoundPos -= shift; |
| } |
| } |
| |
| spho->procState = SPHO_STEPSTATE_INIT; |
| break; |
| |
| default: |
| picoos_emRaiseException(this->common->em, PICO_ERR_OTHER, NULL, NULL); |
| return PICODATA_PU_ERROR; |
| break; |
| |
| } /* switch (spho->procState) */ |
| |
| } /* while (1) */ |
| |
| /* should be never reached */ |
| picoos_emRaiseException(this->common->em, PICO_ERR_OTHER, NULL, NULL); |
| return PICODATA_PU_ERROR; |
| } |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| /* end picospho.c */ |