Home | History | Annotate | Download | only in lib
      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 picoacph.c
     18  *
     19  * accentuation and phrasing
     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 "picobase.h"
     32 #include "picodata.h"
     33 #include "picoacph.h"
     34 #include "picokdt.h"
     35 #include "picoklex.h"
     36 #include "picoktab.h"
     37 
     38 #ifdef __cplusplus
     39 extern "C" {
     40 #endif
     41 #if 0
     42 }
     43 #endif
     44 
     45 /* PU acphStep states */
     46 #define SA_STEPSTATE_COLLECT       0
     47 #define SA_STEPSTATE_PROCESS_PHR  12
     48 #define SA_STEPSTATE_PROCESS_ACC  13
     49 #define SA_STEPSTATE_FEED          2
     50 
     51 
     52 /* boundary strength state */
     53 #define SA_BOUNDSTRENGTH_SSEP      0 /* sentence separator */
     54 #define SA_BOUNDSTRENGTH_PPHR      1 /* primary phrase separator */
     55 
     56 
     57 /*  subobject    : AccPhrUnit
     58  *  shortcut     : acph
     59  *  context size : one phrase, max. 30 non-PUNC items, for non-processed items
     60  *                 one item if internal input empty
     61  */
     62 
     63 /**
     64  * @addtogroup picoacph
     65  *
     66  * <b> Pico Accentuation and Phrasing </b>\n
     67  *
     68   internal buffers:
     69 
     70   - headx : array for extended item heads of fixed size (head plus
     71     index for content, plus two fields for boundary strength/type)
     72   - cbuf : buffer for item contents (referenced by index in
     73     headx).
     74 
     75   0. bottom up filling of items in headx and cbuf
     76 
     77   1. phrasing (right-to-left):
     78 
     79      e.g. from      WP WP WP       WP WP PUNC  WP WP PUNC        WP WP WP PUNC  FLUSH    \n
     80      e.g. to  BSBEG WP WP WP BPHR3 WP WP BPHR1 WP WP BSEND BSBEG WP WP WP BSEND BTERM    \n
     81               |1                         |2                |3                   |4        \n
     82 
     83      2-level bound state: The internal buffer contains one primary phrase (sometimes forced, if buffer
     84      allmost full), with the trailing PUNCT item included (last item).\n
     85      If the trailing PUNC is a a primary phrase separator, the
     86        item is not output, but instead, the bound state is set to PPHR, so that the correct BOUND can
     87        be output at the start of the next primary phrase.\n
     88      Otherwise,
     89        the item is converted to the corresponding BOUND and output. the bound state is set to SSEP,
     90        so that a BOUND of type SBEG is output at the start of the next primary phrase.
     91 
     92      trailing PUNC item       bound states                                    \n
     93                               SSEP           PPHR                            \n
     94        PUNC(SENTEND, X)       B(B,X)>SSEP    B(P1,X)>SSEP  (X = T | Q | E)    \n
     95        PUNC(FLUSH, T)         B(B,T)>SSEP*    B(P1,T)>SSEP                    \n
     96        PUNC(PHRASEEND, P)     B(B,P)>PPHR    B(P1,P)>PPHR                    \n
     97        PUNC(PHRASEEND, FORC)  B(B,P)>PPHR    B(P1,P)>PPHR                    \n
     98 
     99     If more than one sentence separators follow each other (e.g. SEND-FLUSH, SEND-SEND) then
    100      all but the first will be treated as an (empty) phrase containing just this item.
    101      If this (single) item is a flush, creation of SBEG is suppressed.
    102 
    103 
    104   - dtphr phrasing tree ("subphrasing")
    105     determines
    106       - BOUND_PHR2
    107       - BOUND_PHR3
    108   - boundary strenghts are determined for every word (except the
    109     first one) from right-to-left. The boundary types mark the phrase
    110     type of the phrase following the boundary.
    111   - number of items actually changed (new BOUND items added): because
    112     of fixed size without content, two fields are contained in headx
    113     to indicate if a BOUND needs to be added to the LEFT of the item.
    114     -> headx further extended with boundary strength and type info to
    115     indicate that to the left of the headx ele a BOUND needs to be
    116     inserted when outputting.
    117 
    118   2. accentuation:
    119   - number of items unchanged, content unchanged, only head info changes
    120   -> changed in place in headx
    121 */
    122 
    123 
    124 typedef struct {
    125     picodata_itemhead_t head;
    126     picoos_uint16 cind;
    127     picoos_uint8 boundstrength;  /* bstrength to the left, 0 if not set */
    128     picoos_uint8 boundtype;      /* btype for following phrase, 0 if not set */
    129 } picoacph_headx_t;
    130 
    131 
    132 typedef struct acph_subobj {
    133     picoos_uint8 procState; /* for next processing step decision */
    134     picoos_uint8 boundStrengthState;    /* boundary strength state */
    135 
    136     picoos_uint8 inspaceok;      /* flag: headx/cbuf has space for an item */
    137     picoos_uint8 needsmoreitems; /* flag: need more items */
    138 
    139     picoos_uint8 tmpbuf[PICODATA_MAX_ITEMSIZE];  /* tmp. location for an item */
    140 
    141     picoacph_headx_t headx[PICOACPH_MAXNR_HEADX];
    142     picoos_uint16 headxBottom; /* bottom */
    143     picoos_uint16 headxLen;    /* length, 0 if empty */
    144 
    145     picoos_uint8 cbuf[PICOACPH_MAXSIZE_CBUF];
    146     picoos_uint16 cbufBufSize; /* actually allocated size */
    147     picoos_uint16 cbufLen;     /* length, 0 if empty */
    148 
    149     /* tab knowledge base */
    150     picoktab_Phones tabphones;
    151 
    152     /* dtphr knowledge base */
    153     picokdt_DtPHR dtphr;
    154 
    155     /* dtacc knowledge base */
    156     picokdt_DtACC dtacc;
    157 } acph_subobj_t;
    158 
    159 
    160 static pico_status_t acphInitialize(register picodata_ProcessingUnit this, picoos_int32 resetMode) {
    161     acph_subobj_t * acph;
    162     picoos_uint16 i;
    163 
    164     PICODBG_DEBUG(("calling"));
    165 
    166     if (NULL == this || NULL == this->subObj) {
    167         return picoos_emRaiseException(this->common->em,
    168                                        PICO_ERR_NULLPTR_ACCESS, NULL, NULL);
    169     }
    170     acph = (acph_subobj_t *) this->subObj;
    171     acph->procState = SA_STEPSTATE_COLLECT;
    172     acph->boundStrengthState = SA_BOUNDSTRENGTH_SSEP;
    173 
    174     acph->inspaceok = TRUE;
    175     acph->needsmoreitems = TRUE;
    176 
    177     acph->headxBottom = 0;
    178     acph->headxLen = 0;
    179     acph->cbufBufSize = PICOACPH_MAXSIZE_CBUF;
    180     acph->cbufLen = 0;
    181 
    182     /* init headx, cbuf */
    183     for (i = 0; i < PICOACPH_MAXNR_HEADX; i++){
    184         acph->headx[i].head.type = 0;
    185         acph->headx[i].head.info1 = 0;
    186         acph->headx[i].head.info2 = 0;
    187         acph->headx[i].head.len = 0;
    188         acph->headx[i].cind = 0;
    189         acph->headx[i].boundstrength = 0;
    190         acph->headx[i].boundtype = 0;
    191     }
    192     for (i = 0; i < PICOACPH_MAXSIZE_CBUF; i++) {
    193         acph->cbuf[i] = 0;
    194     }
    195 
    196     if (resetMode == PICO_RESET_SOFT) {
    197         /*following initializations needed only at startup or after a full reset*/
    198         return PICO_OK;
    199     }
    200 
    201     /* kb tabphones */
    202     acph->tabphones =
    203         picoktab_getPhones(this->voice->kbArray[PICOKNOW_KBID_TAB_PHONES]);
    204     if (acph->tabphones == NULL) {
    205         return picoos_emRaiseException(this->common->em, PICO_EXC_KB_MISSING,
    206                                        NULL, NULL);
    207     }
    208     PICODBG_DEBUG(("got tabphones"));
    209 
    210 #ifdef PICO_DEBUG_1
    211     {
    212         picoos_uint16 itmp;
    213         for (itmp = 0; itmp < 256; itmp++) {
    214             if (picoktab_hasVowelProp(acph->tabphones, itmp)) {
    215                 PICODBG_DEBUG(("tabphones hasVowel: %d", itmp));
    216             }
    217             if (picoktab_hasDiphthProp(acph->tabphones, itmp)) {
    218                 PICODBG_DEBUG(("tabphones hasDiphth: %d", itmp));
    219             }
    220             if (picoktab_hasGlottProp(acph->tabphones, itmp)) {
    221                 PICODBG_DEBUG(("tabphones hasGlott: %d", itmp));
    222             }
    223             if (picoktab_hasNonsyllvowelProp(acph->tabphones, itmp)) {
    224                 PICODBG_DEBUG(("tabphones hasNonsyllvowel: %d", itmp));
    225             }
    226             if (picoktab_hasSyllconsProp(acph->tabphones, itmp)) {
    227                 PICODBG_DEBUG(("tabphones hasSyllcons: %d", itmp));
    228             }
    229 
    230             if (picoktab_isPrimstress(acph->tabphones, itmp)) {
    231                 PICODBG_DEBUG(("tabphones isPrimstress: %d", itmp));
    232             }
    233             if (picoktab_isSecstress(acph->tabphones, itmp)) {
    234                 PICODBG_DEBUG(("tabphones isSecstress: %d", itmp));
    235             }
    236             if (picoktab_isSyllbound(acph->tabphones, itmp)) {
    237                 PICODBG_DEBUG(("tabphones isSyllbound: %d", itmp));
    238             }
    239             if (picoktab_isPause(acph->tabphones, itmp)) {
    240                 PICODBG_DEBUG(("tabphones isPause: %d", itmp));
    241             }
    242         }
    243 
    244         PICODBG_DEBUG(("tabphones primstressID: %d",
    245                        picoktab_getPrimstressID(acph->tabphones)));
    246         PICODBG_DEBUG(("tabphones secstressID: %d",
    247                        picoktab_getSecstressID(acph->tabphones)));
    248         PICODBG_DEBUG(("tabphones syllboundID: %d",
    249                        picoktab_getSyllboundID(acph->tabphones)));
    250         PICODBG_DEBUG(("tabphones pauseID: %d",
    251                        picoktab_getPauseID(acph->tabphones)));
    252     }
    253 #endif
    254 
    255 
    256     /* kb dtphr */
    257     acph->dtphr = picokdt_getDtPHR(this->voice->kbArray[PICOKNOW_KBID_DT_PHR]);
    258     if (acph->dtphr == NULL) {
    259         return picoos_emRaiseException(this->common->em, PICO_EXC_KB_MISSING,
    260                                        NULL, NULL);
    261     }
    262     PICODBG_DEBUG(("got dtphr"));
    263 
    264     /* kb dtacc */
    265     acph->dtacc = picokdt_getDtACC(this->voice->kbArray[PICOKNOW_KBID_DT_ACC]);
    266     if (acph->dtacc == NULL) {
    267         return picoos_emRaiseException(this->common->em, PICO_EXC_KB_MISSING,
    268                                        NULL, NULL);
    269     }
    270     PICODBG_DEBUG(("got dtacc"));
    271 
    272     return PICO_OK;
    273 }
    274 
    275 static picodata_step_result_t acphStep(register picodata_ProcessingUnit this,
    276                                      picoos_int16 mode,
    277                                      picoos_uint16 *numBytesOutput);
    278 
    279 static pico_status_t acphTerminate(register picodata_ProcessingUnit this)
    280 {
    281     return PICO_OK;
    282 }
    283 
    284 static pico_status_t acphSubObjDeallocate(register picodata_ProcessingUnit this,
    285                                         picoos_MemoryManager mm) {
    286     mm = mm;        /* avoid warning "var not used in this function"*/
    287     if (NULL != this) {
    288         picoos_deallocate(this->common->mm, (void *) &this->subObj);
    289     }
    290     return PICO_OK;
    291 }
    292 
    293 
    294 picodata_ProcessingUnit picoacph_newAccPhrUnit(picoos_MemoryManager mm,
    295                                               picoos_Common common,
    296                                               picodata_CharBuffer cbIn,
    297                                               picodata_CharBuffer cbOut,
    298                                               picorsrc_Voice voice) {
    299     picodata_ProcessingUnit this;
    300 
    301     this = picodata_newProcessingUnit(mm, common, cbIn, cbOut, voice);
    302     if (this == NULL) {
    303         return NULL;
    304     }
    305 
    306     this->initialize = acphInitialize;
    307     PICODBG_DEBUG(("set this->step to acphStep"));
    308     this->step = acphStep;
    309     this->terminate = acphTerminate;
    310     this->subDeallocate = acphSubObjDeallocate;
    311     this->subObj = picoos_allocate(mm, sizeof(acph_subobj_t));
    312     if (this->subObj == NULL) {
    313         picoos_deallocate(mm, (void *)&this);
    314         picoos_emRaiseException(common->em, PICO_EXC_OUT_OF_MEM, NULL, NULL);
    315         return NULL;
    316     }
    317 
    318     acphInitialize(this, PICO_RESET_FULL);
    319     return this;
    320 }
    321 
    322 
    323 /* ***********************************************************************/
    324 /* PROCESS_PHR/ACC support functions */
    325 /* ***********************************************************************/
    326 
    327 
    328 static picoos_uint8 acphGetNrSylls(register picodata_ProcessingUnit this,
    329                                  register acph_subobj_t *acph,
    330                                  const picoos_uint16 ind) {
    331     picoos_uint8 i;
    332     picoos_uint8 ch;
    333     picoos_uint8 count;
    334 
    335     count = 1;
    336     for (i = 0; i < acph->headx[ind].head.len; i++) {
    337         ch = acph->cbuf[acph->headx[ind].cind + i];
    338         if (picoktab_isSyllbound(acph->tabphones, ch)) {
    339             count++;
    340         }
    341     }
    342     return count;
    343 }
    344 
    345 
    346 /* ***********************************************************************/
    347 /* PROCESS_PHR functions */
    348 /* ***********************************************************************/
    349 
    350 
    351 /* find next POS to the left of 'ind' and return its POS and index */
    352 static picoos_uint8 acphPhrItemSeqGetPosLeft(register picodata_ProcessingUnit this,
    353                                            register acph_subobj_t *acph,
    354                                            const picoos_uint16 ind,
    355                                            picoos_uint16 *leftind) {
    356     picoos_uint8 val;
    357     picoos_int32 i;
    358 
    359     val = PICOKDT_EPSILON;
    360     for (i = ind - 1; ((val == PICOKDT_EPSILON) && (i >= 0)); i--) {
    361         if ((acph->headx[i].head.type == PICODATA_ITEM_WORDPHON)) {
    362             val = acph->headx[i].head.info1;
    363         }
    364     }
    365     *leftind = i + 1;
    366     return val;
    367 }
    368 
    369 
    370 /* right-to-left, for each WORDPHON do phr */
    371 static pico_status_t acphSubPhrasing(register picodata_ProcessingUnit this,
    372                                    register acph_subobj_t *acph) {
    373     picokdt_classify_result_t dtres;
    374     picoos_uint8 valbuf[5];
    375     picoos_uint16 nrwordspre;
    376     picoos_uint16 nrwordsfol;
    377     picoos_uint16 nrsyllsfol;
    378     picoos_uint16 lastprev2; /* last index of POS(es) found to the left */
    379     picoos_uint8 curpos;     /* POS(es) of current word */
    380     picoos_uint16 upbound;   /* index of last WORDPHON item (with POS) */
    381     picoos_uint8 okay;
    382     picoos_uint8 nosubphrases;
    383     picoos_int32 i;
    384 
    385     /* set initial values */
    386     okay = TRUE;
    387     nosubphrases = TRUE;
    388     curpos = PICOKDT_EPSILON;   /* needs to be in 2^8 */
    389 
    390     /* set upbound to last WORDPHON, don't worry about first one */
    391     upbound = acph->headxLen - 1;
    392     while ((upbound > 0) &&
    393            (acph->headx[upbound].head.type != PICODATA_ITEM_WORDPHON)) {
    394         upbound--;
    395     }
    396 
    397     /* zero or one WORDPHON, no subphrasing needed, but handling of
    398        BOUND strength state is needed */
    399     if (upbound <= 0) {
    400         /* phrase not containing more than one WORDPHON */
    401         PICODBG_DEBUG(("less than two WORDPHON in phrase -> no subphrasing"));
    402     }
    403 
    404     lastprev2 = upbound;
    405 
    406     /* set initial nr pre/fol words/sylls, upbound is ind of last WORDPHON */
    407     nrwordsfol = 0;
    408     nrsyllsfol = 0;
    409     nrwordspre = 0;
    410     for (i = 0; i < upbound; i++) {
    411         if (acph->headx[i].head.type == PICODATA_ITEM_WORDPHON) {
    412             nrwordspre++;
    413         }
    414     }
    415 
    416     nrwordspre++;    /* because we later have a decrement before being used */
    417 
    418 
    419     /* set POS of current word in valbuf[1], will be shifted right afterwards */
    420     valbuf[1] = acph->headx[upbound].head.info1;
    421     /* find first POS to the left and set valbuf[0] */
    422     valbuf[0] = acphPhrItemSeqGetPosLeft(this, acph, lastprev2, &lastprev2);
    423     for (i = 2; i < 5; i++) {
    424         valbuf[i] = PICOKDT_EPSILON;
    425     }
    426 
    427     PICODBG_TRACE(("headxLen: %d", acph->headxLen));
    428 
    429     /* at least two WORDPHON items */
    430     /* process from right-to-left all items in headx, except for 1st WORDPHON */
    431     for (i = upbound; (i > 0) && (nrwordspre > 1); i--) {
    432         okay = TRUE;
    433 
    434         PICODBG_TRACE(("iter: %d, type: %c", i, acph->headx[i].head.type));
    435 
    436         /* if not (WORDPHON) */
    437         if ((acph->headx[i].head.type != PICODATA_ITEM_WORDPHON)) {
    438             continue;
    439         }
    440 
    441         PICODBG_TRACE(("iter: %d, curpos: %d", i, acph->headx[i].head.info1));
    442 
    443         /* get and set POS of current item, must be WORDPHON */
    444         curpos = acph->headx[i].head.info1;
    445 
    446         /* no continue so far => at [i] we have a WORDPHON item */
    447         /* shift all POS elements one position to the right */
    448         valbuf[4] = valbuf[3];
    449         valbuf[3] = valbuf[2];
    450         valbuf[2] = valbuf[1];
    451         valbuf[1] = valbuf[0];
    452         /* find next POS to the left and set valbuf[0] */
    453         valbuf[0] = acphPhrItemSeqGetPosLeft(this, acph, lastprev2, &lastprev2);
    454 
    455         /* better check double than never */
    456         if (curpos != valbuf[2]) {
    457             PICODBG_WARN(("syncing POS"));
    458             picoos_emRaiseWarning(this->common->em, PICO_WARN_INVECTOR,
    459                                   NULL, NULL);
    460             valbuf[2] = curpos;
    461         }
    462 
    463         nrwordsfol++;
    464         nrsyllsfol += acphGetNrSylls(this, acph, i);
    465         nrwordspre--;
    466 
    467         PICODBG_TRACE(("%d: [%d,%d|%d|%d,%d|%d,%d,%d]",
    468                        i, valbuf[0], valbuf[1], valbuf[2], valbuf[3],
    469                        valbuf[4], nrwordspre, nrwordsfol, nrsyllsfol));
    470 
    471         /* no continue so far => subphrasing needed */
    472         /* construct input vector, which is set in dtphr */
    473         if (!picokdt_dtPHRconstructInVec(acph->dtphr, valbuf[0], valbuf[1],
    474                                          valbuf[2], valbuf[3], valbuf[4],
    475                                          nrwordspre, nrwordsfol, nrsyllsfol)) {
    476             /* error constructing invec */
    477             PICODBG_WARN(("problem with invec"));
    478             picoos_emRaiseWarning(this->common->em, PICO_WARN_INVECTOR,
    479                                   NULL, NULL);
    480             okay = FALSE;
    481         }
    482         /* classify */
    483         if (okay && (!picokdt_dtPHRclassify(acph->dtphr))) {
    484             /* error doing classification */
    485             PICODBG_WARN(("problem classifying"));
    486             picoos_emRaiseWarning(this->common->em, PICO_WARN_CLASSIFICATION,
    487                                   NULL, NULL);
    488             okay = FALSE;
    489         }
    490         /* decompose */
    491         if (okay && (!picokdt_dtPHRdecomposeOutClass(acph->dtphr, &dtres))) {
    492             /* error decomposing */
    493             PICODBG_WARN(("problem decomposing"));
    494             picoos_emRaiseWarning(this->common->em, PICO_WARN_OUTVECTOR,
    495                                   NULL, NULL);
    496             okay = FALSE;
    497         }
    498 
    499         if (okay && dtres.set) {
    500             PICODBG_DEBUG(("%d - inpos: %d, out: %d", i,valbuf[2],dtres.class));
    501         } else {
    502             PICODBG_WARN(("problem determining subphrase boundary strength"));
    503             dtres.class = PICODATA_ITEMINFO1_ERR;
    504         }
    505 
    506         if (dtres.class > 255) {
    507             PICODBG_WARN(("dt class outside valid range, setting to PHR0"));
    508             dtres.class = PICODATA_ITEMINFO1_BOUND_PHR0;
    509         }
    510         acph->headx[i].boundstrength = (picoos_uint8)dtres.class;
    511         if ((dtres.class == PICODATA_ITEMINFO1_BOUND_PHR2) ||
    512             (dtres.class == PICODATA_ITEMINFO1_BOUND_PHR3)) {
    513             if (nosubphrases) {
    514                 /* it's the last secondary phrase in the primary phrase */
    515                 /* add type info */
    516                 switch (acph->headx[acph->headxLen - 1].head.info2) {
    517                     case PICODATA_ITEMINFO2_PUNC_SENT_T:
    518                         acph->headx[i].boundtype =
    519                             PICODATA_ITEMINFO2_BOUNDTYPE_T;
    520                         break;
    521                     case PICODATA_ITEMINFO2_PUNC_SENT_Q:
    522                         acph->headx[i].boundtype =
    523                             PICODATA_ITEMINFO2_BOUNDTYPE_Q;
    524                         break;
    525                     case PICODATA_ITEMINFO2_PUNC_SENT_E:
    526                         acph->headx[i].boundtype =
    527                             PICODATA_ITEMINFO2_BOUNDTYPE_E;
    528                         break;
    529                     case PICODATA_ITEMINFO2_PUNC_PHRASE:
    530                     case PICODATA_ITEMINFO2_PUNC_PHRASE_FORCED:
    531                         acph->headx[i].boundtype =
    532                             PICODATA_ITEMINFO2_BOUNDTYPE_P;
    533                         break;
    534                     default:
    535                         PICODBG_WARN(("invalid boundary type, not set"));
    536                         break;
    537                 }
    538                 nosubphrases = FALSE;
    539 
    540             } else {
    541                 acph->headx[i].boundtype =
    542                     PICODATA_ITEMINFO2_BOUNDTYPE_P;
    543             }
    544             /* reset nr following words and sylls counters */
    545             nrwordsfol = 0;
    546             nrsyllsfol = 0;
    547         }
    548     }
    549 
    550     /* process first item, add bound-info */
    551     switch (acph->boundStrengthState) {
    552         case SA_BOUNDSTRENGTH_SSEP:
    553             acph->headx[0].boundstrength =
    554                 PICODATA_ITEMINFO1_BOUND_SBEG;
    555             break;
    556         case SA_BOUNDSTRENGTH_PPHR:
    557             acph->headx[0].boundstrength =
    558                 PICODATA_ITEMINFO1_BOUND_PHR1;
    559             break;
    560         default:
    561             PICODBG_WARN(("invalid boundary strength, not set"));
    562             break;
    563     }
    564 
    565     /* set boundary strength state */
    566     switch (acph->headx[acph->headxLen - 1].head.info1) {
    567         case PICODATA_ITEMINFO1_PUNC_SENTEND:
    568         case PICODATA_ITEMINFO1_PUNC_FLUSH:
    569             acph->boundStrengthState = SA_BOUNDSTRENGTH_SSEP;
    570             break;
    571         case PICODATA_ITEMINFO1_PUNC_PHRASEEND:
    572             acph->boundStrengthState = SA_BOUNDSTRENGTH_PPHR;
    573             break;
    574         default:
    575             PICODBG_WARN(("invalid boundary strength state, not changed"));
    576             break;
    577     }
    578 
    579     if (nosubphrases) {
    580         /* process first item, add type info */
    581         switch (acph->headx[acph->headxLen - 1].head.info2) {
    582             case PICODATA_ITEMINFO2_PUNC_SENT_T:
    583                 acph->headx[0].boundtype =
    584                     PICODATA_ITEMINFO2_BOUNDTYPE_T;
    585                 break;
    586             case PICODATA_ITEMINFO2_PUNC_SENT_Q:
    587                 acph->headx[0].boundtype =
    588                     PICODATA_ITEMINFO2_BOUNDTYPE_Q;
    589                 break;
    590             case PICODATA_ITEMINFO2_PUNC_SENT_E:
    591                 acph->headx[0].boundtype =
    592                     PICODATA_ITEMINFO2_BOUNDTYPE_E;
    593                 break;
    594             case PICODATA_ITEMINFO2_PUNC_PHRASE:
    595             case PICODATA_ITEMINFO2_PUNC_PHRASE_FORCED:
    596                 acph->headx[0].boundtype =
    597                     PICODATA_ITEMINFO2_BOUNDTYPE_P;
    598                 break;
    599             default:
    600                 PICODBG_WARN(("invalid boundary type, not set"));
    601                 break;
    602         }
    603     } else {
    604         acph->headx[0].boundtype =
    605             PICODATA_ITEMINFO2_BOUNDTYPE_P;
    606     }
    607 
    608     return PICO_OK;
    609 }
    610 
    611 
    612 /* ***********************************************************************/
    613 /* PROCESS_ACC functions */
    614 /* ***********************************************************************/
    615 
    616 /* find next POS to the left of 'ind' and return its POS and index */
    617 static picoos_uint8 acphAccItemSeqGetPosLeft(register picodata_ProcessingUnit this,
    618                                            register acph_subobj_t *acph,
    619                                            const picoos_uint16 ind,
    620                                            picoos_uint16 *leftind) {
    621     picoos_uint8 val;
    622     picoos_int32 i;
    623 
    624     val = PICOKDT_EPSILON;
    625     for (i = ind - 1; ((val == PICOKDT_EPSILON) && (i >= 0)); i--) {
    626         if ((acph->headx[i].head.type == PICODATA_ITEM_WORDPHON)) {
    627             val = acph->headx[i].head.info1;
    628         }
    629     }
    630     *leftind = i + 1;
    631     return val;
    632 }
    633 
    634 
    635 /* s1: nr sylls in word before the first primary stressed syll,
    636    s2: nr sylls in word after (but excluding) the first primary stressed syll */
    637 static picoos_uint8 acphAccNrSyllParts(register picodata_ProcessingUnit this,
    638                                      register acph_subobj_t *acph,
    639                                      const picoos_uint16 ind,
    640                                      picoos_uint8 *s1,
    641                                      picoos_uint8 *s2) {
    642     picoos_uint16 pind;
    643     picoos_uint16 pend;    /* phone string start+len */
    644     picoos_uint8 afterprim;
    645 
    646     /* check ind is in valid range */
    647     if (ind >= acph->headxLen) {
    648         return FALSE;
    649     }
    650 
    651     *s1 = 0;
    652     *s2 = 0;
    653     afterprim = FALSE;
    654     pend = acph->headx[ind].cind + acph->headx[ind].head.len;
    655     for (pind = acph->headx[ind].cind; pind < pend; pind++) {
    656         if (picoktab_isPrimstress(acph->tabphones, acph->cbuf[pind])) {
    657             afterprim = TRUE;
    658         } else if (picoktab_isSyllbound(acph->tabphones, acph->cbuf[pind])) {
    659             if (afterprim) {
    660                 (*s2)++;
    661             } else {
    662                 (*s1)++;
    663             }
    664         }
    665     }
    666     if (afterprim) {
    667         (*s2)++;
    668     } else {
    669         (*s1)++;
    670     }
    671 
    672     /* exclude the stressed syllable */
    673     if ((*s2) > 0) {
    674         (*s2)--;
    675     }
    676     /* handle the case when there is no primstress */
    677     if (!afterprim) {
    678         (*s2) = (*s1);
    679     }
    680     return TRUE;
    681 }
    682 
    683 
    684 static picoos_uint8 acphAccGetNrsRight(register picodata_ProcessingUnit this,
    685                                      register acph_subobj_t *acph,
    686                                      const picoos_uint16 ind,
    687                                      picoos_uint16 *nrwordsfol,
    688                                      picoos_uint16 *nrsyllsfol,
    689                                      picoos_uint16 *footwordsfol,
    690                                      picoos_uint16 *footsyllsfol) {
    691     picoos_uint16 i;
    692     picoos_uint8 s1;
    693     picoos_uint8 s2;
    694 
    695     if (!acphAccNrSyllParts(this, acph, ind, &s1, &s2)) {
    696         return FALSE;
    697     }
    698 
    699     *nrwordsfol = 0;
    700     *nrsyllsfol = s2;
    701     i = ind + 1;
    702     while ((i < acph->headxLen) &&
    703            (acph->headx[i].boundstrength == PICODATA_ITEMINFO1_BOUND_PHR0)) {
    704         if (acph->headx[i].head.type == PICODATA_ITEM_WORDPHON) {
    705             (*nrwordsfol)++;
    706             *nrsyllsfol += acphGetNrSylls(this, acph, i);
    707         }
    708         i++;
    709     }
    710 
    711     *footwordsfol = 0;
    712     *footsyllsfol = s2;
    713     i = ind + 1;
    714     while ((i < acph->headxLen) &&
    715            (acph->headx[i].head.info2 != PICODATA_ACC1)) {
    716         if (acph->headx[i].head.type == PICODATA_ITEM_WORDPHON) {
    717             (*footwordsfol)++;
    718             *footsyllsfol += acphGetNrSylls(this, acph, i);
    719         }
    720         i++;
    721     }
    722     if ((i < acph->headxLen) && (acph->headx[i].head.info2 == PICODATA_ACC1)) {
    723         if (!acphAccNrSyllParts(this, acph, i, &s1, &s2)) {
    724             return FALSE;
    725         }
    726         *footsyllsfol += s1;
    727     }
    728     return TRUE;
    729 }
    730 
    731 
    732 static picoos_uint8 acphAccGetNrsLeft(register picodata_ProcessingUnit this,
    733                                     register acph_subobj_t *acph,
    734                                     const picoos_uint16 ind,
    735                                     picoos_uint16 *nrwordspre,
    736                                     picoos_uint16 *nrsyllspre) {
    737     picoos_int32 i;
    738     picoos_uint8 s1;
    739     picoos_uint8 s2;
    740 
    741     if (!acphAccNrSyllParts(this, acph, ind, &s1, &s2)) {
    742         return FALSE;
    743     }
    744 
    745     *nrwordspre = 0;
    746     *nrsyllspre = s1;
    747     i = ind - 1;
    748     while ((i >= 0) &&
    749            (acph->headx[i].boundstrength == PICODATA_ITEMINFO1_BOUND_PHR0)) {
    750         if (acph->headx[i].head.type == PICODATA_ITEM_WORDPHON) {
    751             (*nrwordspre)++;
    752             *nrsyllspre += acphGetNrSylls(this, acph, i);
    753         }
    754         i--;
    755     }
    756 
    757     if ((acph->headx[i].boundstrength != PICODATA_ITEMINFO1_BOUND_PHR0) &&
    758         (acph->headx[i].head.type == PICODATA_ITEM_WORDPHON)) {
    759         (*nrwordspre)++;
    760         *nrsyllspre += acphGetNrSylls(this, acph, i);
    761     }
    762     return TRUE;
    763 }
    764 
    765 
    766 /* return TRUE if wordphon contains no stress, FALSE otherwise */
    767 static picoos_uint8 acphIsWordWithoutStress(register picodata_ProcessingUnit this,
    768                                           register acph_subobj_t *acph,
    769                                           const picoos_uint16 ind) {
    770     picoos_uint8 i;
    771     picoos_uint16 pos;
    772 
    773     pos = acph->headx[ind].cind;
    774     for (i = 0; i < acph->headx[ind].head.len; i++) {
    775         if (picoktab_isPrimstress(acph->tabphones, acph->cbuf[pos + i]) ||
    776             picoktab_isSecstress(acph->tabphones, acph->cbuf[pos + i])) {
    777             return FALSE;
    778         }
    779     }
    780     return TRUE;
    781 }
    782 
    783 
    784 /* right-to-left, for each WORDPHON do acc */
    785 static pico_status_t acphAccentuation(register picodata_ProcessingUnit this,
    786                                     register acph_subobj_t *acph) {
    787     picokdt_classify_result_t dtres;
    788     picoos_uint8 valbuf[5];
    789     picoos_uint16 hist1;
    790     picoos_uint16 hist2;
    791     picoos_uint16 nrwordspre;
    792     picoos_uint16 nrsyllspre;
    793     picoos_uint16 nrwordsfol;
    794     picoos_uint16 nrsyllsfol;
    795     picoos_uint16 footwordsfol;
    796     picoos_uint16 footsyllsfol;
    797     picoos_uint16 lastprev2; /* last index of POS(es) found to the left */
    798     picoos_uint8 curpos;     /* POS(es) of current word */
    799     picoos_uint16 prevout;
    800     picoos_uint8 okay;
    801     picoos_int32 upbound;   /* index of last WORDPHON item (with POS) */
    802     picoos_uint16 i;
    803 
    804     /* set initial values */
    805     okay = TRUE;
    806     curpos = PICOKDT_EPSILON;    /* needs to be < 2^8 */
    807 
    808     /* set upbound to last WORDPHON */
    809     upbound = acph->headxLen - 1;
    810     while ((upbound >= 0) &&
    811            (acph->headx[upbound].head.type != PICODATA_ITEM_WORDPHON)) {
    812         upbound--;
    813     }
    814 
    815     if (upbound < 0) {
    816         /* phrase containing zero WORDPHON */
    817         PICODBG_DEBUG(("no WORDPHON in phrase -> no accentuation"));
    818         return PICO_OK;
    819     }
    820 
    821     lastprev2 = upbound;
    822 
    823     /* set initial history values */
    824     prevout = PICOKDT_HISTORY_ZERO;
    825     hist1 = PICOKDT_HISTORY_ZERO;
    826     hist2 = PICOKDT_HISTORY_ZERO;
    827 
    828     /* set initial nr pre/fol words/sylls, upbound is ind of last WORDPHON */
    829     nrwordsfol = 0;
    830     nrsyllsfol = 0;
    831     footwordsfol = 0;
    832     footsyllsfol = 0;
    833     nrwordspre = 0;
    834     nrsyllspre = 0;
    835 
    836     /* set POS of current word in valbuf[1], will be shifted right afterwards */
    837     valbuf[1] = acph->headx[upbound].head.info1;
    838     /* find first POS to the left and set valbuf[0] */
    839     valbuf[0] = acphAccItemSeqGetPosLeft(this, acph, lastprev2, &lastprev2);
    840     for (i = 2; i < 5; i++) {
    841         valbuf[i] = PICOKDT_EPSILON;
    842     }
    843 
    844     PICODBG_TRACE(("headxLen: %d", acph->headxLen));
    845 
    846     /* process from right-to-left all items in headx */
    847     for (i = upbound+1; i > 0; ) {
    848         i--;
    849 
    850         okay = TRUE;
    851 
    852         PICODBG_TRACE(("iter: %d, type: %c", i, acph->headx[i].head.type));
    853 
    854         /* if not (WORDPHON) */
    855         if ((acph->headx[i].head.type != PICODATA_ITEM_WORDPHON)) {
    856             continue;
    857         }
    858 
    859         PICODBG_TRACE(("iter: %d, curpos: %d", i, acph->headx[i].head.info1));
    860 
    861         /* get and set POS of current item, must be WORDPHON */
    862         curpos = acph->headx[i].head.info1;
    863 
    864         /* no continue so far => at [i] we have a WORDPHON item */
    865         /* shift all POS elements one position to the right */
    866         valbuf[4] = valbuf[3];
    867         valbuf[3] = valbuf[2];
    868         valbuf[2] = valbuf[1];
    869         valbuf[1] = valbuf[0];
    870         /* find next POS to the left and set valbuf[0] */
    871         valbuf[0] = acphAccItemSeqGetPosLeft(this, acph, lastprev2, &lastprev2);
    872 
    873         /* better check double than never */
    874         if (curpos != valbuf[2]) {
    875             PICODBG_WARN(("syncing POS"));
    876             picoos_emRaiseWarning(this->common->em, PICO_WARN_INVECTOR,
    877                                   NULL, NULL);
    878             valbuf[2] = curpos;
    879         }
    880 
    881         /* set history values */
    882         hist2 = hist1;
    883         hist1 = prevout;
    884 
    885         /* ************************************************************ */
    886         /* many speedups possible by avoiding double calc of attribtues */
    887         /* ************************************************************ */
    888 
    889         /* get distances */
    890         if ((!acphAccGetNrsRight(this, acph, i, &nrwordsfol, &nrsyllsfol,
    891                                &footwordsfol, &footsyllsfol)) ||
    892             (!acphAccGetNrsLeft(this, acph, i, &nrwordspre, &nrsyllspre))) {
    893             PICODBG_WARN(("problem setting distances in invec"));
    894             picoos_emRaiseWarning(this->common->em, PICO_WARN_INVECTOR,
    895                                   NULL, NULL);
    896             okay = FALSE;
    897         }
    898 
    899         PICODBG_TRACE(("%d: [%d,%d,%d,%d,%d|%d,%d|%d,%d,%d,%d|%d,%d]", i,
    900                        valbuf[0], valbuf[1], valbuf[2], valbuf[3], valbuf[4],
    901                        hist1, hist2, nrwordspre, nrsyllspre,
    902                        nrwordsfol, nrsyllsfol, footwordsfol, footsyllsfol));
    903 
    904         /* no continue so far => accentuation needed */
    905         /* construct input vector, which is set in dtacc */
    906         if (!picokdt_dtACCconstructInVec(acph->dtacc, valbuf[0], valbuf[1],
    907                                          valbuf[2], valbuf[3], valbuf[4],
    908                                          hist1, hist2, nrwordspre, nrsyllspre,
    909                                          nrwordsfol, nrsyllsfol, footwordsfol,
    910                                          footsyllsfol)) {
    911             /* error constructing invec */
    912             PICODBG_WARN(("problem with invec"));
    913             picoos_emRaiseWarning(this->common->em, PICO_WARN_INVECTOR,
    914                                   NULL, NULL);
    915             okay = FALSE;
    916         }
    917         /* classify */
    918         if (okay && (!picokdt_dtACCclassify(acph->dtacc, &prevout))) {
    919             /* error doing classification */
    920             PICODBG_WARN(("problem classifying"));
    921             picoos_emRaiseWarning(this->common->em, PICO_WARN_CLASSIFICATION,
    922                                   NULL, NULL);
    923             okay = FALSE;
    924         }
    925         /* decompose */
    926         if (okay && (!picokdt_dtACCdecomposeOutClass(acph->dtacc, &dtres))) {
    927             /* error decomposing */
    928             PICODBG_WARN(("problem decomposing"));
    929             picoos_emRaiseWarning(this->common->em, PICO_WARN_OUTVECTOR,
    930                                   NULL, NULL);
    931             okay = FALSE;
    932         }
    933 
    934         if (dtres.class > 255) {
    935             PICODBG_WARN(("dt class outside valid range, setting to ACC0"));
    936             dtres.class = PICODATA_ACC0;
    937         }
    938 
    939         if (okay && dtres.set) {
    940             PICODBG_DEBUG(("%d - inpos: %d, out: %d", i,valbuf[2],dtres.class));
    941             if (acphIsWordWithoutStress(this, acph, i)) {
    942                 if (dtres.class != PICODATA_ACC0) {
    943                     acph->headx[i].head.info2 = PICODATA_ACC3;
    944                 } else {
    945                     acph->headx[i].head.info2 = (picoos_uint8)dtres.class;
    946                 }
    947             } else {
    948                 acph->headx[i].head.info2 = (picoos_uint8)dtres.class;
    949             }
    950             PICODBG_DEBUG(("%d - after-nostress-corr: %d",
    951                            i, acph->headx[i].head.info2));
    952         } else {
    953             PICODBG_WARN(("problem determining accentuation level"));
    954             dtres.class = PICODATA_ITEMINFO1_ERR;
    955         }
    956     }
    957     return PICO_OK;
    958 }
    959 
    960 
    961 
    962 /* ***********************************************************************/
    963 /* acphStep support functions */
    964 /* ***********************************************************************/
    965 
    966 static picoos_uint8 acphPutBoundItem(register picodata_ProcessingUnit this,
    967                                    register acph_subobj_t *acph,
    968                                    const picoos_uint8 strength,
    969                                    const picoos_uint8 type,
    970                                    picoos_uint8 *dopuoutfull,
    971                                    picoos_uint16 *numBytesOutput) {
    972     pico_status_t rv = PICO_OK;
    973     picoos_uint16 blen = 0;
    974     picodata_itemhead_t tmphead;
    975 
    976     *dopuoutfull = FALSE;
    977 
    978     /* construct BOUND item in tmpbuf and put item */
    979     tmphead.type = PICODATA_ITEM_BOUND;
    980     tmphead.info1 = strength;
    981     tmphead.info2 = type;
    982     tmphead.len = 0;
    983     rv = picodata_put_itemparts(&tmphead, NULL, 0, acph->tmpbuf,
    984                                 PICODATA_MAX_ITEMSIZE, &blen);
    985     if (rv != PICO_OK) {
    986         PICODBG_ERROR(("problem creating BOUND item"));
    987         picoos_emRaiseException(this->common->em, rv, NULL, NULL);
    988         return FALSE;
    989     }
    990     /* put constructed item to ext. charbuf */
    991     rv = picodata_cbPutItem(this->cbOut, acph->tmpbuf, blen, &blen);
    992 
    993     *numBytesOutput += blen;
    994     if (rv == PICO_EXC_BUF_OVERFLOW) {
    995         PICODBG_DEBUG(("overflow in cb output buffer"));
    996         *dopuoutfull = TRUE;    /* ie. do PU_OUT_FULL later */
    997         return FALSE;
    998     } else if (rv != PICO_OK) {
    999         PICODBG_ERROR(("problem putting BOUND item"));
   1000         picoos_emRaiseException(this->common->em, rv, NULL, NULL);
   1001         return FALSE;
   1002     }
   1003 
   1004     PICODATA_INFO_ITEM(this->voice->kbArray[PICOKNOW_KBID_DBG],
   1005                        (picoos_uint8 *)"acph: ", acph->tmpbuf, blen);
   1006 
   1007     return TRUE;
   1008 }
   1009 
   1010 
   1011 
   1012 /* ***********************************************************************/
   1013 /*                          acphStep function                              */
   1014 /* ***********************************************************************/
   1015 
   1016 /*
   1017 complete phrase processed in one step, if not fast enough -> rework
   1018 
   1019 init, collect into internal buffer, process, and then feed to
   1020 output buffer
   1021 
   1022 init state: INIT ext           ext
   1023 state trans:     in hc1  hc2   out
   1024 
   1025 INIT | putItem   =  0    0    +1      | BUSY  -> COLL (put B-SBEG item,
   1026                                                    set do-init to false)
   1027 
   1028                                     inspace-ok-hc1
   1029                                   needs-more-items-(phrase-or-flush)
   1030 COLL1 |getItems -n +n             0 1 | ATOMIC -> PPOSD     (got items,
   1031                                                       if flush set do-init)
   1032 COLL2 |getItems -n +n             1 0 | ATOMIC -> PPOSD (got items, forced)
   1033 COLL3 |getItems -n +n             1 1 | IDLE          (got items, need more)
   1034 COLL4 |getItems  =  =             1 1 | IDLE             (got no items)
   1035 
   1036 PPOSD | posd     = ~n~n               | BUSY     -> PWP     (posd done)
   1037 PWP   | lex/g2p  = ~n-n  0+n          | BUSY     -> PPHR    (lex/g2p done)
   1038 PPHR  | phr      = -n 0 +m=n          | BUSY     -> PACC    (phr done, m>=n)
   1039 PACC  | acc      =  0 0 ~m=n          | BUSY     -> FEED    (acc done)
   1040 
   1041                                   doinit-flag
   1042 FEED | putItems  0  0 0 -m-n  +m  0   | BUSY -> COLL    (put items)
   1043 FEED | putItems  0  0 0 -m-n  +m  1   | BUSY -> INIT    (put items)
   1044 FEED | putItems  0  0 0 -d-d  +d      | OUT_FULL        (put some items)
   1045 */
   1046 
   1047 static picodata_step_result_t acphStep(register picodata_ProcessingUnit this,
   1048                                      picoos_int16 mode,
   1049                                      picoos_uint16 *numBytesOutput) {
   1050     register acph_subobj_t *acph;
   1051     pico_status_t rv = PICO_OK;
   1052     pico_status_t rvP = PICO_OK;
   1053     picoos_uint16 blen = 0;
   1054     picoos_uint16 clen = 0;
   1055     picoos_uint16 i;
   1056 
   1057 
   1058     if (NULL == this || NULL == this->subObj) {
   1059         return PICODATA_PU_ERROR;
   1060     }
   1061     acph = (acph_subobj_t *) this->subObj;
   1062     mode = mode;        /* avoid warning "var not used in this function"*/
   1063     *numBytesOutput = 0;
   1064     while (1) { /* exit via return */
   1065         PICODBG_DEBUG(("doing state %i, hLen|c1Len: %d|%d",
   1066                        acph->procState, acph->headxLen, acph->cbufLen));
   1067 
   1068         switch (acph->procState) {
   1069 
   1070             /* *********************************************************/
   1071             /* collect state: get item(s) from charBuf and store in
   1072              * internal buffers, need a complete punctuation-phrase
   1073              */
   1074             case SA_STEPSTATE_COLLECT:
   1075 
   1076                 while (acph->inspaceok && acph->needsmoreitems && (PICO_OK ==
   1077                 (rv = picodata_cbGetItem(this->cbIn, acph->tmpbuf,
   1078                                 PICODATA_MAX_ITEMSIZE, &blen)))) {
   1079                     rvP = picodata_get_itemparts(acph->tmpbuf,
   1080                     PICODATA_MAX_ITEMSIZE, &(acph->headx[acph->headxLen].head),
   1081                             &(acph->cbuf[acph->cbufLen]), acph->cbufBufSize
   1082                                     - acph->cbufLen, &clen);
   1083                     if (rvP != PICO_OK) {
   1084                         PICODBG_ERROR(("problem getting item parts"));
   1085                         picoos_emRaiseException(this->common->em, rvP,
   1086                         NULL, NULL);
   1087                         return PICODATA_PU_ERROR;
   1088                     }
   1089 
   1090                     /* if CMD(...FLUSH...) -> PUNC(...FLUSH...),
   1091                      construct PUNC-FLUSH item in headx */
   1092                     if ((acph->headx[acph->headxLen].head.type
   1093                             == PICODATA_ITEM_CMD)
   1094                             && (acph->headx[acph->headxLen].head.info1
   1095                                     == PICODATA_ITEMINFO1_CMD_FLUSH)) {
   1096                         acph->headx[acph->headxLen].head.type
   1097                                 = PICODATA_ITEM_PUNC;
   1098                         acph->headx[acph->headxLen].head.info1
   1099                                 = PICODATA_ITEMINFO1_PUNC_FLUSH;
   1100                         acph->headx[acph->headxLen].head.info2
   1101                                 = PICODATA_ITEMINFO2_PUNC_SENT_T;
   1102                         acph->headx[acph->headxLen].head.len = 0;
   1103                     }
   1104 
   1105                     /* check/set needsmoreitems */
   1106                     if (acph->headx[acph->headxLen].head.type
   1107                             == PICODATA_ITEM_PUNC) {
   1108                         acph->needsmoreitems = FALSE;
   1109                     }
   1110 
   1111                     /* check/set inspaceok, keep spare slot for forcing */
   1112                     if ((acph->headxLen >= (PICOACPH_MAXNR_HEADX - 2))
   1113                             || ((acph->cbufBufSize - acph->cbufLen)
   1114                                     < PICODATA_MAX_ITEMSIZE)) {
   1115                         acph->inspaceok = FALSE;
   1116                     }
   1117 
   1118                     if (clen > 0) {
   1119                         acph->headx[acph->headxLen].cind = acph->cbufLen;
   1120                         acph->cbufLen += clen;
   1121                     } else {
   1122                         acph->headx[acph->headxLen].cind = 0;
   1123                     }
   1124                     acph->headxLen++;
   1125                 }
   1126 
   1127                 if (!acph->needsmoreitems) {
   1128                     /* 1, phrase buffered */
   1129                     acph->procState = SA_STEPSTATE_PROCESS_PHR;
   1130                     return PICODATA_PU_ATOMIC;
   1131                 } else if (!acph->inspaceok) {
   1132                     /* 2, forced phrase end */
   1133                     /* at least one slot is still free, use it to
   1134                        force a trailing PUNC item */
   1135                     acph->headx[acph->headxLen].head.type = PICODATA_ITEM_PUNC;
   1136                     acph->headx[acph->headxLen].head.info1 =
   1137                         PICODATA_ITEMINFO1_PUNC_PHRASEEND;
   1138                     acph->headx[acph->headxLen].head.info2 =
   1139                         PICODATA_ITEMINFO2_PUNC_PHRASE_FORCED;
   1140                     acph->headx[acph->headxLen].head.len = 0;
   1141                     acph->needsmoreitems = FALSE; /* not really needed for now */
   1142                     acph->headxLen++;
   1143                     PICODBG_WARN(("forcing phrase end, added PUNC_PHRASEEND"));
   1144                     picoos_emRaiseWarning(this->common->em,
   1145                                           PICO_WARN_FALLBACK, NULL,
   1146                                           (picoos_char *)"forced phrase end");
   1147                     acph->procState = SA_STEPSTATE_PROCESS_PHR;
   1148                     return PICODATA_PU_ATOMIC;
   1149                 } else if (rv == PICO_EOF) {
   1150                     /* 3, 4 */
   1151                     return PICODATA_PU_IDLE;
   1152                 } else if ((rv == PICO_EXC_BUF_UNDERFLOW) ||
   1153                            (rv == PICO_EXC_BUF_OVERFLOW)) {
   1154                     /* error, no valid item in cb (UNDER) */
   1155                     /*        or tmpbuf not large enough, not possible (OVER) */
   1156                     /* no exception raised, left for ctrl to handle */
   1157                     PICODBG_ERROR(("buffer under/overflow, rv: %d", rv));
   1158                     return PICODATA_PU_ERROR;
   1159                 } else {
   1160                     /* error, only possible if cbGetItem implementation
   1161                        changes without this function being adapted*/
   1162                     PICODBG_ERROR(("untreated return value, rv: %d", rv));
   1163                     return PICODATA_PU_ERROR;
   1164                 }
   1165                 break;
   1166 
   1167 
   1168 
   1169 
   1170             /* *********************************************************/
   1171             /* process phr state: process items in headx and modify
   1172              * headx in place
   1173              */
   1174             case SA_STEPSTATE_PROCESS_PHR:
   1175                 /* ensure there is an item in inBuf */
   1176                 if (acph->headxLen > 0) {
   1177                     /* we have a phrase in headx, cbuf1 (can be
   1178                        single PUNC item), do phrasing and modify headx */
   1179 
   1180                     if (PICO_OK != acphSubPhrasing(this, acph)) {
   1181                         picoos_emRaiseException(this->common->em,
   1182                                                 PICO_ERR_OTHER, NULL, NULL);
   1183                         return PICODATA_PU_ERROR;
   1184                     }
   1185                     acph->procState = SA_STEPSTATE_PROCESS_ACC;
   1186                 } else if (acph->headxLen == 0) {    /* no items in inBuf */
   1187                     PICODBG_WARN(("no items in inBuf"));
   1188                     acph->procState = SA_STEPSTATE_COLLECT;
   1189                     return PICODATA_PU_BUSY;
   1190                 }
   1191 
   1192 #if defined (PICO_DEBUG_NOTNEEDED)
   1193                 if (1) {
   1194                     picoos_uint8 i, j, ittype;
   1195                     for (i = 0; i < acph->headxLen; i++) {
   1196                         if ((acph->headx[i].boundstrength != 0) &&
   1197                             (acph->headx[i].boundstrength !=
   1198                              PICODATA_ITEMINFO1_BOUND_PHR0)) {
   1199                             PICODBG_INFO(("acph-p: boundstrength '%c', "
   1200                                           "boundtype '%c'",
   1201                                           acph->headx[i].boundstrength,
   1202                                           acph->headx[i].boundtype));
   1203                         }
   1204 
   1205                         ittype = acph->headx[i].head.type;
   1206                         PICODBG_INFO_CTX();
   1207                         PICODBG_INFO_MSG(("acph-p: ("));
   1208                         PICODBG_INFO_MSG(("'%c',", ittype));
   1209                         if ((32 <= acph->headx[i].head.info1) &&
   1210                             (acph->headx[i].head.info1 < 127) &&
   1211                             (ittype != PICODATA_ITEM_WORDPHON)) {
   1212                             PICODBG_INFO_MSG(("'%c',",acph->headx[i].head.info1));
   1213                         } else {
   1214                             PICODBG_INFO_MSG(("%3d,", acph->headx[i].head.info1));
   1215                         }
   1216                         if ((32 <= acph->headx[i].head.info2) &&
   1217                             (acph->headx[i].head.info2 < 127)) {
   1218                             PICODBG_INFO_MSG(("'%c',",acph->headx[i].head.info2));
   1219                         } else {
   1220                             PICODBG_INFO_MSG(("%3d,", acph->headx[i].head.info2));
   1221                         }
   1222                         PICODBG_INFO_MSG(("%3d)", acph->headx[i].head.len));
   1223 
   1224                         for (j = 0; j < acph->headx[i].head.len; j++) {
   1225                             if ((ittype == PICODATA_ITEM_CMD)) {
   1226                                 PICODBG_INFO_MSG(("%c",
   1227                                         acph->cbuf[acph->headx[i].cind+j]));
   1228                             } else {
   1229                                 PICODBG_INFO_MSG(("%4d",
   1230                                         acph->cbuf[acph->headx[i].cind+j]));
   1231                             }
   1232                         }
   1233                         PICODBG_INFO_MSG(("\n"));
   1234                     }
   1235                 }
   1236 #endif
   1237 
   1238                 break;
   1239 
   1240 
   1241             /* *********************************************************/
   1242             /* process acc state: process items in headx and modify
   1243              * headx in place
   1244              */
   1245             case SA_STEPSTATE_PROCESS_ACC:
   1246                 /* ensure there is an item in inBuf */
   1247                 if (acph->headxLen > 0) {
   1248                     /* we have a phrase in headx, cbuf (can be
   1249                        single PUNC item), do accentuation and modify headx */
   1250                     if (PICO_OK != acphAccentuation(this, acph)) {
   1251                         picoos_emRaiseException(this->common->em,
   1252                                                 PICO_ERR_OTHER, NULL, NULL);
   1253                         return PICODATA_PU_ERROR;
   1254                     }
   1255                     acph->procState = SA_STEPSTATE_FEED;
   1256                 } else if (acph->headxLen == 0) {    /* no items in inBuf */
   1257                     PICODBG_WARN(("no items in inBuf"));
   1258                     acph->procState = SA_STEPSTATE_COLLECT;
   1259                     return PICODATA_PU_BUSY;
   1260                 }
   1261                 break;
   1262 
   1263 
   1264             /* *********************************************************/
   1265             /* feed state: copy item in internal outBuf to output charBuf */
   1266             case SA_STEPSTATE_FEED: {
   1267                 picoos_uint16 indupbound;
   1268                 picoos_uint8 dopuoutfull;
   1269 
   1270                 PICODBG_DEBUG(("put out items (bot, len): (%d, %d)",
   1271                                acph->headxBottom, acph->headxLen));
   1272 
   1273                 indupbound = acph->headxBottom + acph->headxLen;
   1274                 dopuoutfull = FALSE;
   1275 
   1276                 if (acph->headxBottom == 0) {
   1277                     /* construct first BOUND item in tmpbuf and put item */
   1278                     /* produce BOUND unless it is followed by a term/flush) */
   1279                     if (acph->headx[0].head.info1
   1280                             != PICODATA_ITEMINFO1_PUNC_FLUSH) {
   1281                         if (!acphPutBoundItem(this, acph,
   1282                                 acph->headx[0].boundstrength,
   1283                                 acph->headx[0].boundtype, &dopuoutfull,
   1284                                 numBytesOutput)) {
   1285                             if (dopuoutfull) {
   1286                                 PICODBG_DEBUG(("feeding overflow"));
   1287                                 return PICODATA_PU_OUT_FULL;
   1288                             } else {
   1289                                 /* ERR-msg and exception done in acphPutBoundItem */
   1290                                 return PICODATA_PU_ERROR;
   1291                             }
   1292                         }
   1293                     }
   1294                 }
   1295 
   1296                 /* for all items in headx, cbuf */
   1297                 for (i = acph->headxBottom; i < indupbound; i++) {
   1298 
   1299                     switch (acph->headx[i].head.type) {
   1300                         case PICODATA_ITEM_PUNC:
   1301                             /* if sentence end, put SEND bound */
   1302                             if ((acph->headx[i].head.info1 ==
   1303                                  PICODATA_ITEMINFO1_PUNC_SENTEND) &&
   1304                                 (i == (indupbound - 1))) {
   1305                                 /* construct and put BOUND item */
   1306                                 if (!acphPutBoundItem(this, acph,
   1307                                             PICODATA_ITEMINFO1_BOUND_SEND,
   1308                                             PICODATA_ITEMINFO2_NA,
   1309                                             &dopuoutfull, numBytesOutput)) {
   1310                                     if (dopuoutfull) {
   1311                                         PICODBG_DEBUG(("feeding overflow"));
   1312                                         return PICODATA_PU_OUT_FULL;
   1313                                     } else {
   1314                                         /* ERR-msg and exception done
   1315                                            in acphPutBoundItem */
   1316                                         return PICODATA_PU_ERROR;
   1317                                     }
   1318                                 }
   1319                             } else if ((acph->headx[i].head.info1 ==
   1320                                  PICODATA_ITEMINFO1_PUNC_FLUSH) &&
   1321                                 (i == (indupbound - 1))) {
   1322                                 /* construct and put BOUND item */
   1323                                 if (!acphPutBoundItem(this, acph,
   1324                                             PICODATA_ITEMINFO1_BOUND_TERM,
   1325                                             PICODATA_ITEMINFO2_NA,
   1326                                             &dopuoutfull, numBytesOutput)) {
   1327                                     if (dopuoutfull) {
   1328                                         PICODBG_DEBUG(("feeding overflow"));
   1329                                         return PICODATA_PU_OUT_FULL;
   1330                                     } else {
   1331                                         /* ERR-msg and exception done
   1332                                            in acphPutBoundItem */
   1333                                         return PICODATA_PU_ERROR;
   1334                                     }
   1335                                 }
   1336                             }
   1337                             /* else, good-bye PUNC, not needed anymore */
   1338                             break;
   1339                         default:
   1340 
   1341                             /* PHR2/3 maybe existing, check and add
   1342                                BOUND item now, if needed */
   1343                             if ((acph->headx[i].boundstrength ==
   1344                                  PICODATA_ITEMINFO1_BOUND_PHR2) ||
   1345                                 (acph->headx[i].boundstrength ==
   1346                                  PICODATA_ITEMINFO1_BOUND_PHR3)) {
   1347                                 if (!acphPutBoundItem(this, acph,
   1348                                             acph->headx[i].boundstrength,
   1349                                             acph->headx[i].boundtype,
   1350                                             &dopuoutfull, numBytesOutput)) {
   1351                                     if (dopuoutfull) {
   1352                                         PICODBG_DEBUG(("feeding overflow"));
   1353                                         return PICODATA_PU_OUT_FULL;
   1354                                     } else {
   1355                                         /* ERR-msg and exception done
   1356                                            in acphPutBoundItem */
   1357                                         return PICODATA_PU_ERROR;
   1358                                     }
   1359                                 }
   1360                             }
   1361 
   1362                             /* copy item unmodified */
   1363                             rv = picodata_put_itemparts(&(acph->headx[i].head),
   1364                                      &(acph->cbuf[acph->headx[i].cind]),
   1365                                      acph->headx[i].head.len,
   1366                                      acph->tmpbuf, PICODATA_MAX_ITEMSIZE,
   1367                                      &blen);
   1368 
   1369                             rvP = picodata_cbPutItem(this->cbOut, acph->tmpbuf,
   1370                                     PICODATA_MAX_ITEMSIZE, &clen);
   1371 
   1372                             *numBytesOutput += clen;
   1373 
   1374                             PICODBG_DEBUG(("put item, status: %d", rvP));
   1375 
   1376                             if (rvP == PICO_OK) {
   1377                                 acph->headxBottom++;
   1378                                 acph->headxLen--;
   1379                             } else if (rvP == PICO_EXC_BUF_OVERFLOW) {
   1380                                 /* try again next time, but PHR2/3
   1381                                    bound already added if existing,
   1382                                    ensure it's not output a 2nd
   1383                                    time */
   1384                                 PICODBG_DEBUG(("feeding overflow"));
   1385                                 acph->headx[i].boundstrength = 0;
   1386                                 return PICODATA_PU_OUT_FULL;
   1387                             } else {
   1388                                 /* error, should never happen */
   1389                                 PICODBG_ERROR(("untreated return value, rvP: %d", rvP));
   1390                                 return PICODATA_PU_ERROR;
   1391                             }
   1392 
   1393                             PICODATA_INFO_ITEM(this->voice->kbArray[PICOKNOW_KBID_DBG],
   1394                                                (picoos_uint8 *)"acph: ",
   1395                                                acph->tmpbuf, PICODATA_MAX_ITEMSIZE);
   1396 
   1397                             break;
   1398                     } /*switch*/
   1399                 } /*for*/
   1400 
   1401                 /* reset headx, cbuf */
   1402                 acph->headxBottom = 0;
   1403                 acph->headxLen = 0;
   1404                 acph->cbufLen = 0;
   1405                 for (i = 0; i < PICOACPH_MAXNR_HEADX; i++) {
   1406                     acph->headx[i].boundstrength = 0;
   1407                 }
   1408 
   1409                 /* reset collect state support variables */
   1410                 acph->inspaceok = TRUE;
   1411                 acph->needsmoreitems = TRUE;
   1412 
   1413                 acph->procState = SA_STEPSTATE_COLLECT;
   1414                 return PICODATA_PU_BUSY;
   1415                 break;
   1416             }
   1417 
   1418             default:
   1419                 break;
   1420         } /* switch */
   1421 
   1422     } /* while */
   1423 
   1424     /* should be never reached */
   1425     PICODBG_ERROR(("reached end of function"));
   1426     picoos_emRaiseException(this->common->em, PICO_ERR_OTHER, NULL, NULL);
   1427     return PICODATA_PU_ERROR;
   1428 }
   1429 
   1430 #ifdef __cplusplus
   1431 }
   1432 #endif
   1433 
   1434 
   1435 /* end */
   1436