You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3777 lines
110 KiB
3777 lines
110 KiB
/*******************************************************************************
|
|
* Frontend.cpp *
|
|
*--------------*
|
|
* Description:
|
|
* This module is the main implementation file for the CFrontend class.
|
|
*-------------------------------------------------------------------------------
|
|
* Created By: mc Date: 03/12/99
|
|
* Copyright (C) 1999 Microsoft Corporation
|
|
* All Rights Reserved
|
|
*
|
|
*******************************************************************************/
|
|
|
|
//--- Additional includes
|
|
#include "stdafx.h"
|
|
#ifndef __spttseng_h__
|
|
#include "spttseng.h"
|
|
#endif
|
|
#ifndef Frontend_H
|
|
#include "Frontend.h"
|
|
#endif
|
|
#ifndef SPDebug_h
|
|
#include "spdebug.h"
|
|
#endif
|
|
#ifndef FeedChain_H
|
|
#include "FeedChain.h"
|
|
#endif
|
|
#ifndef AlloOps_H
|
|
#include "AlloOps.h"
|
|
#endif
|
|
#include "sapi.h"
|
|
|
|
#include "StdSentEnum.h"
|
|
|
|
|
|
//-----------------------------
|
|
// Data.cpp
|
|
//-----------------------------
|
|
extern const short g_IPAToAllo[];
|
|
extern const float g_RateScale[];
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::CFrontend *
|
|
*----------------------*
|
|
* Description:
|
|
*
|
|
********************************************************************** MC ***/
|
|
CFrontend::CFrontend()
|
|
{
|
|
SPDBG_FUNC( "CFrontend::CFrontend" );
|
|
m_pUnits = NULL;
|
|
m_unitCount = 0;
|
|
m_CurUnitIndex = 0;
|
|
m_pAllos = NULL;
|
|
m_pSrcObj = NULL;
|
|
} /* CFrontend::CFrontend */
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::~CFrontend *
|
|
*-----------------------*
|
|
* Description:
|
|
*
|
|
********************************************************************** MC ***/
|
|
CFrontend::~CFrontend()
|
|
{
|
|
SPDBG_FUNC( "CFrontend::~CFrontend" );
|
|
|
|
DisposeUnits();
|
|
if( m_pAllos )
|
|
{
|
|
delete m_pAllos;
|
|
m_pAllos = NULL;
|
|
}
|
|
DeleteTokenList();
|
|
} /* CFrontend::~CFrontend */
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::CntrlToRatio *
|
|
*-------------------------*
|
|
* Description:
|
|
* Return rate ratio from control
|
|
*
|
|
********************************************************************** MC ***/
|
|
float CFrontend::CntrlToRatio( long rateControl )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::CntrlToRatio" );
|
|
float rateRatio;
|
|
|
|
if( rateControl < 0 )
|
|
{
|
|
//--------------------------------
|
|
// DECREASE the rate
|
|
//--------------------------------
|
|
if( rateControl < MIN_USER_RATE )
|
|
{
|
|
rateControl = MIN_USER_RATE; // clip to min
|
|
}
|
|
rateRatio = 1.0f / ::g_RateScale[0 - rateControl];
|
|
}
|
|
else
|
|
{
|
|
//--------------------------------
|
|
// INCREASE the rate
|
|
//--------------------------------
|
|
if( rateControl > MAX_USER_RATE )
|
|
{
|
|
rateControl = MAX_USER_RATE; // clip to max
|
|
}
|
|
rateRatio = ::g_RateScale[rateControl];
|
|
}
|
|
|
|
return rateRatio;
|
|
} /* CFrontend::CntrlToRatio */
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::Init *
|
|
*-----------------*
|
|
* Description:
|
|
* Init voice dependent variables, call once when object is created+++
|
|
*
|
|
********************************************************************** MC ***/
|
|
HRESULT CFrontend::Init( IMSVoiceData* pVoiceDataObj, CFeedChain *pSrcObj, MSVOICEINFO* pVoiceInfo )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::Init" );
|
|
HRESULT hr = S_OK;
|
|
|
|
m_pSrcObj = pSrcObj;
|
|
m_BasePitch = (float)pVoiceInfo->Pitch;
|
|
m_pVoiceDataObj = pVoiceDataObj;
|
|
m_ProsodyGain = ((float)pVoiceInfo->ProsodyGain) / 100.0f;
|
|
m_SampleRate = (float)pVoiceInfo->SampleRate;
|
|
|
|
// NOTE: move these to voice data?
|
|
// m_VoiceWPM = pVoiceInfo->Rate;
|
|
// m_PitchRange = pVoiceInfo->PitchRange;
|
|
m_VoiceWPM = 180;
|
|
m_PitchRange = 0.40f; // +/- 0.5 octave
|
|
|
|
|
|
m_RateRatio_API = m_RateRatio_PROSODY = 1.0f;
|
|
|
|
return hr;
|
|
} /* CFrontend::Init */
|
|
|
|
|
|
|
|
|
|
|
|
static ULONG IPA_to_Allo( WCHAR* pSrc, ALLO_CODE* pDest )
|
|
{
|
|
ULONG iIpa, iAllo, i;
|
|
ULONG gotMatch; // for debugging
|
|
|
|
iIpa = iAllo = 0;
|
|
while( pSrc[iIpa] > 0 )
|
|
{
|
|
gotMatch = false;
|
|
//-----------------------------------------
|
|
// ...then search for single word IPA's
|
|
//-----------------------------------------
|
|
for( i = 0; i < NUMBER_OF_ALLO; i++ )
|
|
{
|
|
if( pSrc[iIpa] == g_IPAToAllo[i] )
|
|
{
|
|
pDest[iAllo] = (ALLO_CODE)i;
|
|
gotMatch = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( gotMatch )
|
|
{
|
|
iAllo++;
|
|
}
|
|
/*else
|
|
{
|
|
// Should NEVER get here. Unsupported IPA unicode!
|
|
// Ignore it and go on.
|
|
}*/
|
|
|
|
//----------------------------------
|
|
// Clip at max length
|
|
//----------------------------------
|
|
if( iAllo >= (SP_MAX_PRON_LENGTH-1) )
|
|
{
|
|
iAllo = SP_MAX_PRON_LENGTH-1;
|
|
break;
|
|
}
|
|
iIpa++;
|
|
}
|
|
return iAllo;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::AlloToUnit *
|
|
*-----------------------*
|
|
* Description:
|
|
* Transform ALLO stream into backend UNIT stream+++
|
|
*
|
|
********************************************************************** MC ***/
|
|
HRESULT CFrontend::AlloToUnit( CAlloList *pAllos, UNITINFO *pu )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::AlloToUnit" );
|
|
bool bFirstPass;
|
|
long msPhon, attr;
|
|
ULONG numOfCells;
|
|
CAlloCell *pCurCell, *pNextCell;
|
|
HRESULT hr = S_OK;
|
|
|
|
bFirstPass = true;
|
|
numOfCells = pAllos->GetCount();
|
|
pCurCell = pAllos->GetHeadCell();
|
|
pNextCell = pAllos->GetNextCell();
|
|
while( pCurCell )
|
|
{
|
|
//--------------------------------------
|
|
// Get next allo ID
|
|
//--------------------------------------
|
|
if( pNextCell )
|
|
{
|
|
pu->NextAlloID = (USHORT)pNextCell->m_allo;
|
|
}
|
|
else
|
|
{
|
|
pu->NextAlloID = _SIL_;
|
|
}
|
|
|
|
//--------------------------------------
|
|
// Convert to Whistler phon code
|
|
//--------------------------------------
|
|
attr = 0;
|
|
if( pCurCell->m_ctrlFlags & PRIMARY_STRESS )
|
|
{
|
|
attr |= ALLO_IS_STRESSED;
|
|
}
|
|
hr = m_pVoiceDataObj->AlloToUnit( (short)pCurCell->m_allo, attr, &msPhon );
|
|
if( FAILED(hr) )
|
|
{
|
|
//------------------------
|
|
// allo ID is invalid
|
|
//------------------------
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
pu->PhonID = msPhon;
|
|
pu->AlloID = (USHORT)pCurCell->m_allo;
|
|
pu->flags = 0;
|
|
pu->AlloFeatures = 0;
|
|
pu->ctrlFlags = pCurCell->m_ctrlFlags;
|
|
//--------------------------------------
|
|
// Flag WORD boundary
|
|
//--------------------------------------
|
|
if( pCurCell->m_ctrlFlags & WORD_START )
|
|
{
|
|
pu->flags |= WORD_START_FLAG;
|
|
//----------------------------------------------
|
|
// Remember source word position and length
|
|
//----------------------------------------------
|
|
pu->srcPosition = pCurCell->m_SrcPosition;
|
|
pu->srcLen = pCurCell->m_SrcLen;
|
|
}
|
|
|
|
//----------------------------------------------------
|
|
// Flag SENTENCE boundary on 1st displayable word
|
|
//----------------------------------------------------
|
|
if( bFirstPass && (pCurCell->m_SentenceLen > 0) )
|
|
{
|
|
bFirstPass = false;
|
|
pu->flags |= SENT_START_FLAG;
|
|
//----------------------------------------------
|
|
// Remember source word position and length
|
|
//----------------------------------------------
|
|
pu->sentencePosition = pCurCell->m_SentencePosition;
|
|
pu->sentenceLen = pCurCell->m_SentenceLen;
|
|
}
|
|
|
|
pu->nKnots = KNOTS_PER_PHON;
|
|
/*for( k = 0; k < pu->nKnots; k++ )
|
|
{
|
|
pu->pTime[k] = pCurCell->m_ftTime[k] * m_SampleRate;
|
|
pu->pF0[k] = pCurCell->m_ftPitch[k];
|
|
pu->pAmp[k] = pu->ampRatio;
|
|
}*/
|
|
|
|
//----------------------------
|
|
// Controls and events
|
|
//----------------------------
|
|
pu->user_Volume = pCurCell->m_user_Volume;
|
|
pu->pBMObj = (void*)pCurCell->m_pBMObj;
|
|
pCurCell->m_pBMObj = NULL;
|
|
|
|
//----------------------------------------
|
|
// Pass features for viseme event
|
|
//----------------------------------------
|
|
if( pCurCell->m_ctrlFlags & PRIMARY_STRESS )
|
|
{
|
|
pu->AlloFeatures |= SPVFEATURE_STRESSED;
|
|
}
|
|
if( pCurCell->m_ctrlFlags & EMPHATIC_STRESS )
|
|
{
|
|
pu->AlloFeatures |= SPVFEATURE_EMPHASIS;
|
|
}
|
|
|
|
pu->duration = PITCH_BUF_RES;
|
|
|
|
pu->silenceSource = pCurCell->m_SilenceSource;
|
|
pu++;
|
|
}
|
|
pCurCell = pNextCell;
|
|
pNextCell = pAllos->GetNextCell();
|
|
}
|
|
return hr;
|
|
} /* CFrontend::AlloToUnit */
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::PrepareSpeech *
|
|
*--------------------------*
|
|
* Description:
|
|
* Prepare frontend for new speech
|
|
*
|
|
********************************************************************** MC ***/
|
|
void CFrontend::PrepareSpeech( IEnumSpSentence* pEnumSent, ISpTTSEngineSite *pOutputSite )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::PrepareSpeech" );
|
|
|
|
m_pEnumSent = pEnumSent;
|
|
m_SpeechState = SPEECH_CONTINUE;
|
|
m_CurUnitIndex = m_unitCount = 0;
|
|
m_HasSpeech = false;
|
|
m_pOutputSite = pOutputSite;
|
|
m_fInQuoteProsody = m_fInParenProsody = false;
|
|
m_CurPitchOffs = 0;
|
|
m_CurPitchRange = 1.0;
|
|
m_RateRatio_PROSODY = 1.0f;
|
|
} /* CFrontend::PrepareSpeech */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* IsTokenPunct *
|
|
*--------------*
|
|
* Description:
|
|
* Return TRUE if char is , . ! or ?
|
|
*
|
|
********************************************************************** MC ***/
|
|
bool fIsPunctuation( TTSSentItem Item )
|
|
{
|
|
SPDBG_FUNC( "IsTokenPunct" );
|
|
|
|
return ( Item.pItemInfo->Type == eCOMMA ||
|
|
Item.pItemInfo->Type == eSEMICOLON ||
|
|
Item.pItemInfo->Type == eCOLON ||
|
|
Item.pItemInfo->Type == ePERIOD ||
|
|
Item.pItemInfo->Type == eQUESTION ||
|
|
Item.pItemInfo->Type == eEXCLAMATION );
|
|
} /* fIsPunctuation */
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::ToBISymbols *
|
|
*------------------------*
|
|
* Description:
|
|
* Label each word with ToBI prosody notation+++
|
|
*
|
|
********************************************************************** MC ***/
|
|
HRESULT CFrontend::ToBISymbols()
|
|
{
|
|
SPDBG_FUNC( "CFrontend::ToBISymbols" );
|
|
TOBI_PHRASE *pTPhrase;
|
|
long i, cPhrases;
|
|
PROSODY_POS prevPOS, curPOS;
|
|
bool possible_YNQ = false;
|
|
long cTok;
|
|
CFEToken *pTok, *pPrevTok, *pAuxTok;
|
|
bool hasEmph = false;
|
|
SPLISTPOS listPos;
|
|
|
|
|
|
//----------------------------------
|
|
// Get memory for phrase array
|
|
//----------------------------------
|
|
pAuxTok = NULL; // To quiet the compiler
|
|
cTok = m_TokList.GetCount();
|
|
if( cTok )
|
|
{
|
|
pTPhrase = new TOBI_PHRASE[cTok]; // worse case: each token is a phrase
|
|
if( pTPhrase )
|
|
{
|
|
//---------------------------------------------
|
|
// Find sub-phrases from POS
|
|
// For now, detect function/content boundaries
|
|
//---------------------------------------------
|
|
hasEmph = false;
|
|
cPhrases = 0;
|
|
i = 0;
|
|
listPos = m_TokList.GetHeadPosition();
|
|
pTok = m_TokList.GetNext( listPos );
|
|
prevPOS = pTok->m_posClass;
|
|
while( pTok->phon_Str[0] == _SIL_ )
|
|
{
|
|
if( i >= (cTok-1) )
|
|
{
|
|
break;
|
|
}
|
|
i++;
|
|
if( listPos != NULL )
|
|
{
|
|
pTok = m_TokList.GetNext( listPos );
|
|
}
|
|
}
|
|
if( pTok->m_posClass == POS_AUX )
|
|
{
|
|
//---------------------------------
|
|
// Could be a yes/no question
|
|
//---------------------------------
|
|
possible_YNQ = true;
|
|
pAuxTok = pTok;
|
|
}
|
|
pTPhrase[cPhrases].start = i;
|
|
for( ; i < cTok; i++ )
|
|
{
|
|
curPOS = pTok->m_posClass;
|
|
if( (curPOS != prevPOS) && (pTok->phon_Str[0] != _SIL_) )
|
|
{
|
|
pTPhrase[cPhrases].posClass = prevPOS;
|
|
pTPhrase[cPhrases].end = i-1;
|
|
cPhrases++;
|
|
pTPhrase[cPhrases].start = i;
|
|
prevPOS = curPOS;
|
|
}
|
|
if( pTok->user_Emph > 0 )
|
|
{
|
|
hasEmph = true;
|
|
}
|
|
if( listPos != NULL )
|
|
{
|
|
pTok = m_TokList.GetNext( listPos );
|
|
}
|
|
}
|
|
//-------------------------------
|
|
// Complete last phrase
|
|
//-------------------------------
|
|
pTPhrase[cPhrases].posClass = prevPOS;
|
|
pTPhrase[cPhrases].end = i-1;
|
|
cPhrases++;
|
|
|
|
for( i = 0; i < cPhrases; i++ )
|
|
{
|
|
//-------------------------------------------------------
|
|
// Sequence of function words, place a low tone
|
|
// on the LAST word in a func sequence,
|
|
// if there are more than 1 words in the sequence.
|
|
//-------------------------------------------------------
|
|
if( ((pTPhrase[i].posClass == POS_FUNC) || (pTPhrase[i].posClass == POS_AUX)) &&
|
|
(pTPhrase[i].end - pTPhrase[i].start) )
|
|
{
|
|
pTok = (CFEToken*)m_TokList.GetAt( m_TokList.FindIndex( pTPhrase[i].end ));
|
|
if( pTok->m_Accent == K_NOACC )
|
|
{
|
|
pTok->m_Accent = K_LSTAR;
|
|
pTok->m_Accent_Prom = 2;
|
|
pTok->m_AccentSource = ACC_FunctionSeq;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------
|
|
// Sequence of content words, place a high or
|
|
// rising tone, of random prominence,
|
|
// on the FIRST word in the content sequence
|
|
//-------------------------------------------------------
|
|
else if ( ((pTPhrase[i].posClass == POS_CONTENT) || (pTPhrase[i].posClass == POS_UNK)) )
|
|
{
|
|
pTok = (CFEToken*)m_TokList.GetAt( m_TokList.FindIndex( pTPhrase[i].start ));
|
|
if( pTok->m_Accent == K_NOACC )
|
|
{
|
|
pTok->m_Accent = K_HSTAR;
|
|
pTok->m_Accent_Prom = rand() % 5;
|
|
pTok->m_AccentSource = ACC_ContentSeq;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
delete pTPhrase;
|
|
|
|
//-----------------------------------------
|
|
// Now, insert the BOUNDARY tags
|
|
//-----------------------------------------
|
|
listPos = m_TokList.GetHeadPosition();
|
|
pPrevTok = m_TokList.GetNext( listPos );
|
|
for( i = 1; i < cTok; i++ )
|
|
{
|
|
pTok = m_TokList.GetNext( listPos );
|
|
//--------------------------------
|
|
// Place a terminal boundary
|
|
//--------------------------------
|
|
if( pTok->m_TuneBoundaryType != NULL_BOUNDARY )
|
|
{
|
|
switch( pTok->m_TuneBoundaryType )
|
|
{
|
|
case YN_QUEST_BOUNDARY:
|
|
{
|
|
pPrevTok->m_Accent = K_LSTAR;
|
|
pPrevTok->m_Accent_Prom = 10;
|
|
pPrevTok->m_Boundary = K_HMINUSHPERC;
|
|
pPrevTok->m_Boundary_Prom = 10;
|
|
//-- Diagnostic
|
|
if( pPrevTok->m_AccentSource == ACC_NoSource )
|
|
{
|
|
pPrevTok->m_AccentSource = ACC_YNQuest;
|
|
}
|
|
//-- Diagnostic
|
|
if( pPrevTok->m_BoundarySource == BND_NoSource )
|
|
{
|
|
pPrevTok->m_BoundarySource = BND_YNQuest;
|
|
}
|
|
//-------------------------------------------------------
|
|
// Accent an aux verb in initial position (possible ynq)
|
|
//-------------------------------------------------------
|
|
if( possible_YNQ )
|
|
{
|
|
pAuxTok->m_Accent = K_HSTAR;
|
|
pAuxTok->m_Accent_Prom = 5;
|
|
pAuxTok->m_AccentSource = ACC_InitialVAux;
|
|
}
|
|
}
|
|
break;
|
|
case WH_QUEST_BOUNDARY:
|
|
case DECLAR_BOUNDARY:
|
|
case EXCLAM_BOUNDARY:
|
|
{
|
|
if (pPrevTok->m_posClass == POS_CONTENT)
|
|
{
|
|
pPrevTok->m_Accent = K_HSTAR;
|
|
pPrevTok->m_Accent_Prom = 4;
|
|
//-- Diagnostic
|
|
if( pPrevTok->m_AccentSource == ACC_NoSource )
|
|
{
|
|
pPrevTok->m_AccentSource = ACC_Period;
|
|
}
|
|
}
|
|
pPrevTok->m_Boundary = K_LMINUSLPERC;
|
|
pPrevTok->m_Boundary_Prom = 10;
|
|
//--- Diagnostic
|
|
if( pPrevTok->m_BoundarySource == BND_NoSource )
|
|
{
|
|
pPrevTok->m_BoundarySource = BND_Period;
|
|
}
|
|
}
|
|
break;
|
|
case PHRASE_BOUNDARY:
|
|
{
|
|
if (pPrevTok->m_posClass == POS_CONTENT)
|
|
{
|
|
pPrevTok->m_Accent = K_LHSTAR;
|
|
pPrevTok->m_Accent_Prom = 10;
|
|
//-- Diagnostic
|
|
if( pPrevTok->m_AccentSource == ACC_NoSource )
|
|
{
|
|
pPrevTok->m_AccentSource = ACC_Comma;
|
|
}
|
|
}
|
|
pPrevTok->m_Boundary = K_LMINUSHPERC;
|
|
pPrevTok->m_Boundary_Prom = 5;
|
|
//-- Diagnostic
|
|
if( pPrevTok->m_BoundarySource == BND_NoSource )
|
|
{
|
|
pPrevTok->m_BoundarySource = BND_Comma;
|
|
}
|
|
}
|
|
break;
|
|
case NUMBER_BOUNDARY:
|
|
{
|
|
pPrevTok->m_Boundary = K_LMINUSHPERC;
|
|
pPrevTok->m_Boundary_Prom = 5;
|
|
//-- Diagnostic
|
|
if( pPrevTok->m_BoundarySource == BND_NoSource )
|
|
{
|
|
pPrevTok->m_BoundarySource = BND_NumberTemplate;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
// Use comma for all other boundaries
|
|
if (pPrevTok->m_posClass == POS_CONTENT)
|
|
{
|
|
pPrevTok->m_Accent = K_LHSTAR;
|
|
pPrevTok->m_Accent_Prom = 10;
|
|
//-- Diagnostic
|
|
if( pPrevTok->m_AccentSource == ACC_NoSource )
|
|
{
|
|
pPrevTok->m_AccentSource = pTok->m_AccentSource;
|
|
}
|
|
}
|
|
pPrevTok->m_Boundary = K_LMINUSHPERC;
|
|
pPrevTok->m_Boundary_Prom = 5;
|
|
//-- Diagnostic
|
|
if( pPrevTok->m_BoundarySource == BND_NoSource )
|
|
{
|
|
pPrevTok->m_BoundarySource = pTok->m_BoundarySource;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
pPrevTok = pTok;
|
|
}
|
|
|
|
//--------------------------------------------
|
|
// Loop through each word and increase
|
|
// pitch prominence if EMPHASIZED and
|
|
// decrease prominence for all others
|
|
//--------------------------------------------
|
|
if( hasEmph )
|
|
{
|
|
SPLISTPOS listPos;
|
|
|
|
pPrevTok = NULL;
|
|
listPos = m_TokList.GetHeadPosition();
|
|
while( listPos )
|
|
{
|
|
pTok = m_TokList.GetNext( listPos );
|
|
//------------------------------
|
|
// Is this word emphasized?
|
|
//------------------------------
|
|
if( pTok->user_Emph > 0 )
|
|
{
|
|
//------------------------------
|
|
// Add my clever H*+L*™ tag
|
|
//------------------------------
|
|
pTok->m_Accent = K_HSTARLSTAR;
|
|
pTok->m_Accent_Prom = 10;
|
|
pTok->m_Boundary = K_NOBND; // Delete any boundary tag here...
|
|
if( pPrevTok )
|
|
{
|
|
pPrevTok->m_Boundary = K_NOBND; // ...or before
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//-----------------------------------
|
|
// Is non-emphasized word accented?
|
|
//-----------------------------------
|
|
if( (pTok->m_Accent != K_NOACC) && (pTok->m_Accent_Prom > 5) )
|
|
{
|
|
//------------------------------
|
|
// Then clip its prominence at 5
|
|
//------------------------------
|
|
pTok->m_Accent_Prom = 5;
|
|
}
|
|
//------------------------------
|
|
// Is it a boundary?
|
|
//------------------------------
|
|
/*if( (pTok->m_Boundary != K_NOBND) && (pTok->m_Boundary_Prom > 5) )
|
|
{
|
|
//------------------------------
|
|
// Then clip its prominence at 5
|
|
//------------------------------
|
|
pTok->m_Boundary_Prom = 5;
|
|
}*/
|
|
}
|
|
pPrevTok = pTok;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return S_OK;
|
|
} /* ToBISymbols */
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::TokensToAllo *
|
|
*------------------------*
|
|
* Description:
|
|
* Transform TOKENS into ALLOS
|
|
*
|
|
********************************************************************** MC ***/
|
|
HRESULT CFrontend::TokensToAllo( CFETokenList *pTokList, CAlloList *pAllo )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::TokToAllo" );
|
|
CAlloCell *pLastCell;
|
|
long i;
|
|
long cTok;
|
|
CFEToken *pCurToken, *pNextToken, *pPrevTok;
|
|
SPLISTPOS listPos;
|
|
|
|
|
|
pLastCell = pAllo->GetTailCell(); // Get end (silence)
|
|
if( pLastCell )
|
|
{
|
|
pPrevTok = NULL;
|
|
listPos = pTokList->GetHeadPosition();
|
|
pCurToken = pTokList->GetNext( listPos );
|
|
cTok = pTokList->GetCount();
|
|
for( i = 0; i < cTok; i++ )
|
|
{
|
|
//----------------------------
|
|
// Get NEXT word
|
|
//----------------------------
|
|
if( i < (cTok -1) )
|
|
{
|
|
pNextToken = pTokList->GetNext( listPos );
|
|
}
|
|
else
|
|
{
|
|
pNextToken = NULL;
|
|
}
|
|
if( pAllo->WordToAllo( pPrevTok, pCurToken, pNextToken, pLastCell ) )
|
|
{
|
|
m_HasSpeech = true;
|
|
}
|
|
//----------------------------
|
|
// Bump the pipeline
|
|
//----------------------------
|
|
pPrevTok = pCurToken;
|
|
pCurToken = pNextToken;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
} /* CFrontend::TokensToAllo */
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::GetItemControls *
|
|
*----------------------------*
|
|
* Description:
|
|
* Set user control values from Sent Enum item.
|
|
********************************************************************** MC ***/
|
|
void CFrontend::GetItemControls( const SPVSTATE* pXmlState, CFEToken* pToken )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::GetItemControls" );
|
|
|
|
pToken->user_Volume = pXmlState->Volume;
|
|
pToken->user_Rate = pXmlState->RateAdj;
|
|
pToken->user_Pitch = pXmlState->PitchAdj.MiddleAdj;
|
|
pToken->user_Emph = pXmlState->EmphAdj;
|
|
pToken->m_DurScale = CntrlToRatio( pToken->user_Rate );
|
|
if( (pToken->m_DurScale * m_RateRatio_API * m_RateRatio_PROSODY)
|
|
< DISCRETE_BKPT )
|
|
{
|
|
//-- If the total rate is low enough, insert breaks between words
|
|
pToken->m_TermSil = 0.050f /
|
|
(pToken->m_DurScale * m_RateRatio_API * m_RateRatio_PROSODY);
|
|
pToken->m_DurScale = DISCRETE_BKPT;
|
|
}
|
|
else
|
|
{
|
|
pToken->m_TermSil = 0;
|
|
}
|
|
|
|
} /* CFrontend::GetItemControls */
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::GetPOSClass *
|
|
*------------------------*
|
|
* Description:
|
|
* Transform SAPI POS code to func/content/aux class.
|
|
********************************************************************** MC ***/
|
|
PROSODY_POS CFrontend::GetPOSClass( ENGPARTOFSPEECH sapiPOS )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::GetPOSClass" );
|
|
PROSODY_POS posClass;
|
|
|
|
posClass = POS_UNK;
|
|
switch( sapiPOS )
|
|
{
|
|
case MS_Noun:
|
|
case MS_Verb:
|
|
case MS_Adj:
|
|
case MS_Adv:
|
|
case MS_Interjection:
|
|
{
|
|
posClass = POS_CONTENT;
|
|
break;
|
|
}
|
|
case MS_VAux:
|
|
{
|
|
posClass = POS_AUX;
|
|
break;
|
|
}
|
|
case MS_Modifier:
|
|
case MS_Function:
|
|
case MS_Interr:
|
|
case MS_Pron:
|
|
case MS_ObjPron:
|
|
case MS_SubjPron:
|
|
case MS_RelPron:
|
|
case MS_Conj:
|
|
case MS_CConj:
|
|
case MS_Det:
|
|
case MS_Contr:
|
|
case MS_Prep:
|
|
{
|
|
posClass = POS_FUNC;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return posClass;
|
|
} /* CFrontend::GetPOSClass */
|
|
|
|
|
|
|
|
#define QUOTE_HESITATION 100 // Number of msec
|
|
#define PAREN_HESITATION 100 // Number of msec
|
|
#define PAREN_HESITATION_TAIL 100 // Number of msec
|
|
#define EMPH_HESITATION 1 // Number of msec
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::StateQuoteProsody *
|
|
*------------------------------*
|
|
* Description:
|
|
*
|
|
********************************************************************** MC ***/
|
|
bool CFrontend::StateQuoteProsody( CFEToken *pWordTok, TTSSentItem *pSentItem, bool fInsertSil )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::StateQuoteProsody" );
|
|
bool result = false;
|
|
|
|
if( !m_fInParenProsody )
|
|
{
|
|
if( m_fInQuoteProsody )
|
|
{
|
|
//------------------------------
|
|
// Stop quote prosody
|
|
//------------------------------
|
|
m_fInQuoteProsody = false;
|
|
m_CurPitchOffs = 0.0f;
|
|
m_CurPitchRange = 1.0f;
|
|
if( fInsertSil )
|
|
{
|
|
(void)InsertSilenceAtTail( pWordTok, pSentItem, QUOTE_HESITATION );
|
|
pWordTok->m_SilenceSource = SIL_QuoteEnd;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//------------------------------
|
|
// Begin quote prosody
|
|
//------------------------------
|
|
m_fInQuoteProsody = true;
|
|
m_CurPitchOffs = 0.1f;
|
|
m_CurPitchRange = 1.25f;
|
|
if( fInsertSil )
|
|
{
|
|
(void)InsertSilenceAtTail( pWordTok, pSentItem, QUOTE_HESITATION );
|
|
pWordTok->m_SilenceSource = SIL_QuoteStart;
|
|
}
|
|
}
|
|
result = true;
|
|
}
|
|
return result;
|
|
} /* CFrontend::StateQuoteProsody */
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::StartParenProsody *
|
|
*------------------------------*
|
|
* Description:
|
|
*
|
|
********************************************************************** MC ***/
|
|
bool CFrontend::StartParenProsody( CFEToken *pWordTok, TTSSentItem *pSentItem, bool fInsertSil )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::StartParenProsody" );
|
|
bool result = false;
|
|
|
|
if( (!m_fInParenProsody) && (!m_fInQuoteProsody) )
|
|
{
|
|
m_CurPitchOffs = -0.2f;
|
|
m_CurPitchRange = 0.75f;
|
|
m_fInParenProsody = true;
|
|
m_RateRatio_PROSODY = 1.25f;
|
|
if( fInsertSil )
|
|
{
|
|
(void)InsertSilenceAtTail( pWordTok, pSentItem, PAREN_HESITATION );
|
|
pWordTok->m_SilenceSource = SIL_ParenStart;
|
|
}
|
|
result = true;
|
|
}
|
|
return result;
|
|
} /* CFrontend::StartParenProsody */
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::EndParenProsody *
|
|
*----------------------------*
|
|
* Description:
|
|
*
|
|
********************************************************************** MC ***/
|
|
bool CFrontend::EndParenProsody( CFEToken *pWordTok, TTSSentItem *pSentItem, bool fInsertSil )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::EndParenProsody" );
|
|
bool result = false;
|
|
|
|
if( m_fInParenProsody )
|
|
{
|
|
m_fInParenProsody = false;
|
|
m_CurPitchOffs = 0.0f;
|
|
m_CurPitchRange = 1.0f;
|
|
m_RateRatio_PROSODY = 1.0f;
|
|
if( fInsertSil )
|
|
{
|
|
(void)InsertSilenceAtTail( pWordTok, pSentItem, PAREN_HESITATION_TAIL );
|
|
pWordTok->m_SilenceSource = SIL_ParenStart;
|
|
}
|
|
result = true;
|
|
}
|
|
return result;
|
|
} /* CFrontend::EndParenProsody */
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::InsertSilenceAtTail *
|
|
*--------------------------------*
|
|
* Description:
|
|
*
|
|
********************************************************************** MC ***/
|
|
SPLISTPOS CFrontend::InsertSilenceAtTail( CFEToken *pWordTok, TTSSentItem *pSentItem, long msec )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::InsertSilenceAtTail" );
|
|
|
|
if( msec > 0 )
|
|
{
|
|
pWordTok->user_Break = msec;
|
|
}
|
|
pWordTok->phon_Len = 1;
|
|
pWordTok->phon_Str[0] = _SIL_;
|
|
pWordTok->srcPosition = pSentItem->ulItemSrcOffset;
|
|
pWordTok->srcLen = pSentItem->ulItemSrcLen;
|
|
pWordTok->tokStr[0] = 0; // There's no orth for Break
|
|
pWordTok->tokLen = 0;
|
|
pWordTok->m_PitchBaseOffs = m_CurPitchOffs;
|
|
pWordTok->m_PitchRangeScale = m_CurPitchRange;
|
|
pWordTok->m_ProsodyDurScale = m_RateRatio_PROSODY;
|
|
//----------------------------------
|
|
// Advance to next token
|
|
//----------------------------------
|
|
return m_TokList.AddTail( pWordTok );
|
|
} /* CFrontend::InsertSilenceAtTail */
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::InsertSilenceAfterPos *
|
|
*-----------------------------------*
|
|
* Description:
|
|
* Insert silence token AFTER 'position'
|
|
*
|
|
********************************************************************** MC ***/
|
|
SPLISTPOS CFrontend::InsertSilenceAfterPos( CFEToken *pWordTok, SPLISTPOS position )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::InsertSilenceAfterPos" );
|
|
|
|
pWordTok->phon_Len = 1;
|
|
pWordTok->phon_Str[0] = _SIL_;
|
|
pWordTok->srcPosition = 0;
|
|
pWordTok->srcLen = 0;
|
|
pWordTok->tokStr[0] = '+'; // punctuation
|
|
pWordTok->tokStr[1] = 0; // delimiter
|
|
pWordTok->tokLen = 1;
|
|
pWordTok->m_PitchBaseOffs = m_CurPitchOffs;
|
|
pWordTok->m_PitchRangeScale = m_CurPitchRange;
|
|
pWordTok->m_ProsodyDurScale = m_RateRatio_PROSODY;
|
|
pWordTok->m_DurScale = 0;
|
|
//----------------------------------
|
|
// Advance to next token
|
|
//----------------------------------
|
|
return m_TokList.InsertAfter( position, pWordTok );
|
|
} /* CFrontend::InsertSilenceAfterPos */
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::InsertSilenceBeforePos *
|
|
*------------------------------------*
|
|
* Description:
|
|
* Insert silence token BEFORE 'position'
|
|
*
|
|
********************************************************************** MC ***/
|
|
SPLISTPOS CFrontend::InsertSilenceBeforePos( CFEToken *pWordTok, SPLISTPOS position )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::InsertSilenceBeforePos" );
|
|
|
|
pWordTok->phon_Len = 1;
|
|
pWordTok->phon_Str[0] = _SIL_;
|
|
pWordTok->srcPosition = 0;
|
|
pWordTok->srcLen = 0;
|
|
pWordTok->tokStr[0] = '+'; // punctuation
|
|
pWordTok->tokStr[1] = 0; // delimiter
|
|
pWordTok->tokLen = 1;
|
|
pWordTok->m_PitchBaseOffs = m_CurPitchOffs;
|
|
pWordTok->m_PitchRangeScale = m_CurPitchRange;
|
|
pWordTok->m_ProsodyDurScale = m_RateRatio_PROSODY;
|
|
pWordTok->m_DurScale = 0;
|
|
//----------------------------------
|
|
// Advance to next token
|
|
//----------------------------------
|
|
return m_TokList.InsertBefore( position, pWordTok );
|
|
} /* CFrontend::InsertSilenceBeforePos */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define K_ACCENT_PROM ((rand() % 4) + 4)
|
|
#define K_DEACCENT_PROM 5
|
|
#define K_ACCENT K_HSTAR
|
|
#define K_DEACCENT K_NOACC
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::ProsodyTemplates *
|
|
*-----------------------------*
|
|
* Description:
|
|
* Call prosody template function for supported item types.
|
|
*
|
|
********************************************************************** MC ***/
|
|
void CFrontend::ProsodyTemplates( SPLISTPOS clusterPos, TTSSentItem *pSentItem )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::ProsodyTemplates" );
|
|
long cWordCount;
|
|
CFEToken *pClusterTok;
|
|
|
|
switch( pSentItem->pItemInfo->Type )
|
|
{
|
|
//---------------------------------------
|
|
// Numbers
|
|
//---------------------------------------
|
|
case eNUM_ROMAN_NUMERAL:
|
|
case eNUM_ROMAN_NUMERAL_ORDINAL:
|
|
{
|
|
if ( ( (TTSRomanNumeralItemInfo*) pSentItem->pItemInfo )->pNumberInfo->Type != eDATE_YEAR )
|
|
{
|
|
if ( ((TTSNumberItemInfo*)((TTSRomanNumeralItemInfo*)pSentItem->pItemInfo)->pNumberInfo)->pIntegerPart )
|
|
{
|
|
DoIntegerTemplate( &clusterPos,
|
|
(TTSNumberItemInfo*)((TTSRomanNumeralItemInfo*)pSentItem->pItemInfo)->pNumberInfo,
|
|
pSentItem->ulNumWords );
|
|
}
|
|
|
|
if ( ((TTSNumberItemInfo*)((TTSRomanNumeralItemInfo*)pSentItem->pItemInfo)->pNumberInfo)->pDecimalPart )
|
|
{
|
|
DoNumByNumTemplate( &clusterPos,
|
|
((TTSNumberItemInfo*)((TTSRomanNumeralItemInfo*)pSentItem->pItemInfo)->pNumberInfo)->pDecimalPart->ulNumDigits );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case eNUM_CARDINAL:
|
|
case eNUM_DECIMAL:
|
|
case eNUM_ORDINAL:
|
|
case eNUM_MIXEDFRACTION:
|
|
{
|
|
if ( ( (TTSNumberItemInfo*) pSentItem->pItemInfo )->pIntegerPart )
|
|
{
|
|
cWordCount = DoIntegerTemplate( &clusterPos,
|
|
(TTSNumberItemInfo*) pSentItem->pItemInfo,
|
|
pSentItem->ulNumWords );
|
|
}
|
|
|
|
if( ( (TTSNumberItemInfo*) pSentItem->pItemInfo )->pDecimalPart )
|
|
{
|
|
//-----------------------------------------
|
|
// Skip "point" string...
|
|
//-----------------------------------------
|
|
(void) m_TokList.GetNext( clusterPos );
|
|
//-----------------------------------------
|
|
// ...and do single digit prosody
|
|
//-----------------------------------------
|
|
DoNumByNumTemplate( &clusterPos,
|
|
( (TTSNumberItemInfo*) pSentItem->pItemInfo )->pDecimalPart->ulNumDigits );
|
|
}
|
|
|
|
if ( ( (TTSNumberItemInfo*) pSentItem->pItemInfo )->pFractionalPart )
|
|
{
|
|
//-----------------------------------------
|
|
// Skip "and" string...
|
|
//-----------------------------------------
|
|
pClusterTok = m_TokList.GetNext( clusterPos );
|
|
if( pClusterTok->m_Accent == K_NOACC )
|
|
{
|
|
//--------------------------------------
|
|
// Force POS for "and" to noun
|
|
// so phrasing rules don't kick in!
|
|
//--------------------------------------
|
|
pClusterTok->m_Accent = K_DEACCENT;
|
|
pClusterTok->m_Accent_Prom = K_DEACCENT_PROM;
|
|
pClusterTok->POScode = MS_Noun;
|
|
pClusterTok->m_posClass = POS_CONTENT;
|
|
}
|
|
//-----------------------------------------
|
|
// ...and do fraction prosody
|
|
//-----------------------------------------
|
|
cWordCount = DoFractionTemplate( &clusterPos,
|
|
(TTSNumberItemInfo*) pSentItem->pItemInfo,
|
|
pSentItem->ulNumWords );
|
|
}
|
|
}
|
|
break;
|
|
|
|
//---------------------------------------
|
|
// Fraction
|
|
//---------------------------------------
|
|
case eNUM_FRACTION:
|
|
{
|
|
cWordCount = DoFractionTemplate( &clusterPos,
|
|
(TTSNumberItemInfo*) pSentItem->pItemInfo,
|
|
pSentItem->ulNumWords );
|
|
}
|
|
break;
|
|
|
|
//---------------------------------------
|
|
// Money
|
|
//---------------------------------------
|
|
case eNUM_CURRENCY:
|
|
{
|
|
DoCurrencyTemplate( clusterPos, pSentItem );
|
|
}
|
|
break;
|
|
|
|
//---------------------------------------
|
|
// Phone Numbers
|
|
//---------------------------------------
|
|
case eNUM_PHONENUMBER:
|
|
case eNEWNUM_PHONENUMBER:
|
|
{
|
|
DoPhoneNumberTemplate( clusterPos, pSentItem );
|
|
}
|
|
break;
|
|
|
|
//---------------------------------------
|
|
// Time-of-Day
|
|
//---------------------------------------
|
|
case eTIMEOFDAY:
|
|
{
|
|
DoTODTemplate( clusterPos, pSentItem );
|
|
}
|
|
break;
|
|
|
|
case eELLIPSIS:
|
|
{
|
|
CFEToken *pWordTok;
|
|
|
|
pWordTok = new CFEToken;
|
|
if( pWordTok )
|
|
{
|
|
clusterPos = InsertSilenceAtTail( pWordTok, pSentItem, 0 );
|
|
//clusterPos = m_TokList.GetTailPosition( );
|
|
//clusterPos = InsertSilenceAfterPos( pWordTok, clusterPos );
|
|
pWordTok->m_SilenceSource = SIL_Ellipsis;
|
|
pWordTok->m_TuneBoundaryType = ELLIPSIS_BOUNDARY;
|
|
pWordTok->m_BoundarySource = BND_Ellipsis;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
} /* CFrontend::ProsodyTemplates */
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::DoTODTemplate *
|
|
*--------------------------*
|
|
* Description:
|
|
* Prosody template for time-of-day.
|
|
*
|
|
* TODO: Temp kludge - needs more info in TTSTimeOfDayItemInfo
|
|
********************************************************************** MC ***/
|
|
void CFrontend::DoTODTemplate( SPLISTPOS clusterPos, TTSSentItem *pSentItem )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::DoTODTemplate" );
|
|
TTSTimeOfDayItemInfo *pTOD;
|
|
CFEToken *pWordTok;
|
|
CFEToken *pClusterTok;
|
|
SPLISTPOS curPos, nextPos, prevPos;
|
|
|
|
|
|
curPos = nextPos = clusterPos;
|
|
pTOD = (TTSTimeOfDayItemInfo*)&pSentItem->pItemInfo->Type;
|
|
|
|
// Can't do 24 hr because there's no way to tell
|
|
// if it's 1 or 2 digits (18: vs 23:)
|
|
if( !pTOD->fTwentyFourHour )
|
|
{
|
|
//-------------------------------------
|
|
// Get HOUR token
|
|
//-------------------------------------
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
//-------------------------------------
|
|
// Accent hour
|
|
//-------------------------------------
|
|
pClusterTok->m_Accent = K_ACCENT;
|
|
pClusterTok->m_Accent_Prom = K_ACCENT_PROM;
|
|
pClusterTok->m_AccentSource = ACC_TimeOFDay_HR;
|
|
|
|
//---------------------------------
|
|
// Insert SILENCE after hour
|
|
//---------------------------------
|
|
pWordTok = new CFEToken;
|
|
if( pWordTok )
|
|
{
|
|
nextPos = InsertSilenceAfterPos( pWordTok, clusterPos );
|
|
pWordTok->m_SilenceSource = SIL_TimeOfDay_HR;
|
|
pWordTok->m_TuneBoundaryType = NUMBER_BOUNDARY;
|
|
pWordTok->m_BoundarySource = BND_TimeOFDay_HR;
|
|
pWordTok = NULL;
|
|
//----------------------------
|
|
// Skip last digit
|
|
//----------------------------
|
|
if( clusterPos != NULL )
|
|
{
|
|
curPos = nextPos;
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
}
|
|
}
|
|
if( pTOD->fMinutes )
|
|
{
|
|
curPos = nextPos;
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
//-------------------------------------
|
|
// Accent 1st digit for minutes
|
|
//-------------------------------------
|
|
pClusterTok->m_Accent = K_ACCENT;
|
|
pClusterTok->m_Accent_Prom = K_ACCENT_PROM;
|
|
pClusterTok->m_AccentSource = ACC_TimeOFDay_1stMin;
|
|
}
|
|
|
|
if( pTOD->fTimeAbbreviation )
|
|
{
|
|
curPos = prevPos = m_TokList.GetTailPosition( );
|
|
pClusterTok = m_TokList.GetPrev( prevPos );
|
|
pWordTok = new CFEToken;
|
|
if( pWordTok )
|
|
{
|
|
prevPos = InsertSilenceBeforePos( pWordTok, prevPos );
|
|
pWordTok->m_SilenceSource = SIL_TimeOfDay_AB;
|
|
pWordTok->m_TuneBoundaryType = TOD_BOUNDARY;
|
|
pWordTok->m_BoundarySource = BND_TimeOFDay_AB;
|
|
pWordTok = NULL;
|
|
//pClusterTok = m_TokList.GetNext( clusterPos );
|
|
//pClusterTok = m_TokList.GetNext( clusterPos );
|
|
}
|
|
//-------------------------------------
|
|
// Accent "M"
|
|
//-------------------------------------
|
|
pClusterTok = m_TokList.GetNext( curPos );
|
|
pClusterTok->m_Accent = K_ACCENT;
|
|
pClusterTok->m_Accent_Prom = K_ACCENT_PROM;
|
|
pClusterTok->m_AccentSource = ACC_TimeOFDay_M;
|
|
}
|
|
}
|
|
} /* CFrontend::DoTODTemplate */
|
|
|
|
|
|
|
|
|
|
|
|
CFEToken *CFrontend::InsertPhoneSilenceAtSpace( SPLISTPOS *pClusterPos,
|
|
BOUNDARY_SOURCE bndSrc,
|
|
SILENCE_SOURCE silSrc )
|
|
{
|
|
CFEToken *pWordTok;
|
|
SPLISTPOS curPos, nextPos;
|
|
|
|
curPos = nextPos = *pClusterPos;
|
|
//---------------------------------
|
|
// Insert SILENCE after area code
|
|
//---------------------------------
|
|
pWordTok = new CFEToken;
|
|
if( pWordTok )
|
|
{
|
|
nextPos = InsertSilenceBeforePos( pWordTok, curPos );
|
|
pWordTok->m_SilenceSource = silSrc;
|
|
pWordTok->m_TuneBoundaryType = PHONE_BOUNDARY;
|
|
pWordTok->m_BoundarySource = bndSrc;
|
|
pWordTok->m_AccentSource = ACC_PhoneBnd_AREA; // @@@@ ???
|
|
pWordTok = NULL;
|
|
//----------------------------
|
|
// Skip last digit
|
|
//----------------------------
|
|
if( nextPos != NULL )
|
|
{
|
|
curPos = nextPos;
|
|
pWordTok = m_TokList.GetNext( nextPos );
|
|
}
|
|
}
|
|
//pWordTok = m_TokList.GetNext( clusterPos );
|
|
//-----------------------------------------
|
|
// Filter and embedded silences
|
|
//-----------------------------------------
|
|
while( (pWordTok->phon_Str[0] == _SIL_) && (nextPos != NULL) )
|
|
{
|
|
curPos = nextPos;
|
|
pWordTok = m_TokList.GetNext( nextPos );
|
|
}
|
|
*pClusterPos = curPos;
|
|
|
|
return pWordTok;
|
|
}
|
|
|
|
|
|
|
|
|
|
void CFrontend::InsertPhoneSilenceAtEnd( BOUNDARY_SOURCE bndSrc,
|
|
SILENCE_SOURCE silSrc )
|
|
{
|
|
CFEToken *pWordTok;
|
|
SPLISTPOS curPos, nextPos;
|
|
|
|
curPos = m_TokList.GetTailPosition( );
|
|
//---------------------------------
|
|
// Insert SILENCE after area code
|
|
//---------------------------------
|
|
pWordTok = new CFEToken;
|
|
if( pWordTok )
|
|
{
|
|
nextPos = InsertSilenceAfterPos( pWordTok, curPos );
|
|
pWordTok->m_SilenceSource = silSrc;
|
|
pWordTok->m_TuneBoundaryType = PHONE_BOUNDARY;
|
|
pWordTok->m_BoundarySource = bndSrc;
|
|
pWordTok->m_AccentSource = ACC_PhoneBnd_AREA; // @@@@ ???
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::DoPhoneNumberTemplate *
|
|
*----------------------------------*
|
|
* Description:
|
|
* Prosody template for phone numbers.
|
|
*
|
|
********************************************************************** MC ***/
|
|
void CFrontend::DoPhoneNumberTemplate( SPLISTPOS clusterPos, TTSSentItem *pSentItem )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::DoPhoneNumberTemplate" );
|
|
TTSPhoneNumberItemInfo *pFone;
|
|
CFEToken *pClusterTok;
|
|
long cWordCount;
|
|
SPLISTPOS curPos, nextPos;
|
|
|
|
curPos = nextPos = clusterPos;
|
|
pFone = (TTSPhoneNumberItemInfo*)&pSentItem->pItemInfo->Type;
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
//
|
|
// COUNTRY CODE
|
|
//
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
if( pFone->pCountryCode )
|
|
{
|
|
//-------------------------------------
|
|
// Skip "country" and...
|
|
//-------------------------------------
|
|
curPos = nextPos;
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
|
|
//-------------------------------------
|
|
// ...skip "code"
|
|
//-------------------------------------
|
|
curPos = nextPos;
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
|
|
cWordCount = DoIntegerTemplate( &nextPos,
|
|
pFone->pCountryCode,
|
|
pSentItem->ulNumWords );
|
|
pClusterTok = InsertPhoneSilenceAtSpace( &nextPos, BND_Phone_COUNTRY, SIL_Phone_COUNTRY );
|
|
}
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
//
|
|
// "One"
|
|
//
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
if( pFone->fOne )
|
|
{
|
|
//-------------------------------------
|
|
// Skip "One"
|
|
//-------------------------------------
|
|
curPos = nextPos;
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
//-------------------------------------
|
|
// and add silence
|
|
//-------------------------------------
|
|
pClusterTok = InsertPhoneSilenceAtSpace( &nextPos, BND_Phone_ONE, SIL_Phone_ONE );
|
|
|
|
}
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
//
|
|
// AREA CODE
|
|
//
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
if( pFone->pAreaCode )
|
|
{
|
|
|
|
if( (pFone->fIs800) && nextPos )
|
|
{
|
|
//--------------------------
|
|
// Skip digit
|
|
//--------------------------
|
|
curPos = nextPos;
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
//--------------------------
|
|
// Skip "hundred"
|
|
//--------------------------
|
|
curPos = nextPos;
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
if( nextPos )
|
|
{
|
|
pClusterTok = InsertPhoneSilenceAtSpace( &nextPos, BND_Phone_AREA, SIL_Phone_AREA );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//-------------------------------------
|
|
// Skip "area" and...
|
|
//-------------------------------------
|
|
curPos = nextPos;
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
//-------------------------------------
|
|
// ...skip "code"
|
|
//-------------------------------------
|
|
curPos = nextPos;
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
|
|
DoNumByNumTemplate( &nextPos, pFone->pAreaCode->ulNumDigits );
|
|
if( nextPos )
|
|
{
|
|
pClusterTok = InsertPhoneSilenceAtSpace( &nextPos, BND_Phone_AREA, SIL_Phone_AREA );
|
|
}
|
|
}
|
|
}
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
//
|
|
// Digits
|
|
//
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
unsigned long i;
|
|
|
|
for( i = 0; i < pFone->ulNumGroups; i++ )
|
|
{
|
|
DoNumByNumTemplate( &nextPos, pFone->ppGroups[i]->ulNumDigits );
|
|
if( nextPos )
|
|
{
|
|
pClusterTok = InsertPhoneSilenceAtSpace( &nextPos, BND_Phone_DIGITS, SIL_Phone_DIGITS );
|
|
}
|
|
}
|
|
InsertPhoneSilenceAtEnd( BND_Phone_DIGITS, SIL_Phone_DIGITS );
|
|
} /* CFrontend::DoPhoneNumberTemplate */
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::DoCurrencyTemplate *
|
|
*-------------------------------*
|
|
* Description:
|
|
* Prosody template for currency.
|
|
*
|
|
********************************************************************** MC ***/
|
|
void CFrontend::DoCurrencyTemplate( SPLISTPOS clusterPos, TTSSentItem *pSentItem )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::DoCurrencyTemplate" );
|
|
TTSCurrencyItemInfo *pMoney;
|
|
CFEToken *pWordTok;
|
|
CFEToken *pClusterTok = NULL;
|
|
long cWordCount;
|
|
SPLISTPOS curPos, nextPos;
|
|
|
|
pMoney = (TTSCurrencyItemInfo*)&pSentItem->pItemInfo->Type;
|
|
|
|
curPos = nextPos = clusterPos;
|
|
if( pMoney->pPrimaryNumberPart->Type != eNUM_CARDINAL )
|
|
{
|
|
return;
|
|
}
|
|
cWordCount = DoIntegerTemplate( &nextPos,
|
|
pMoney->pPrimaryNumberPart,
|
|
pSentItem->ulNumWords );
|
|
curPos = nextPos;
|
|
if( cWordCount > 1 )
|
|
{
|
|
if( pMoney->fQuantifier )
|
|
{
|
|
if( nextPos != NULL )
|
|
{
|
|
curPos = nextPos;
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
}
|
|
cWordCount--;
|
|
}
|
|
}
|
|
if( cWordCount > 1 )
|
|
{
|
|
//---------------------------------
|
|
// Insert SILENCE after "dollars"
|
|
//---------------------------------
|
|
pWordTok = new CFEToken;
|
|
if( pWordTok )
|
|
{
|
|
nextPos = InsertSilenceAfterPos( pWordTok, curPos );
|
|
pWordTok->m_SilenceSource = SIL_Currency_DOLLAR;
|
|
pWordTok->m_TuneBoundaryType = NUMBER_BOUNDARY;
|
|
pWordTok->m_BoundarySource = BND_Currency_DOLLAR;
|
|
pWordTok = NULL;
|
|
//----------------------------
|
|
// Skip "dollar(s)"
|
|
//----------------------------
|
|
if( nextPos != NULL )
|
|
{
|
|
curPos = nextPos;
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
}
|
|
}
|
|
if( pMoney->pSecondaryNumberPart != NULL )
|
|
{
|
|
//----------------------------
|
|
// Skip SILENCE
|
|
//----------------------------
|
|
if( nextPos != NULL )
|
|
{
|
|
curPos = nextPos;
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
}
|
|
cWordCount--;
|
|
//----------------------------
|
|
// Skip AND
|
|
//----------------------------
|
|
if( nextPos != NULL )
|
|
{
|
|
curPos = nextPos;
|
|
if( pClusterTok->m_Accent == K_NOACC )
|
|
{
|
|
//--------------------------------------
|
|
// Force POS for "and" to noun
|
|
// so phrasing rules don't kick in!
|
|
//--------------------------------------
|
|
pClusterTok->m_Accent = K_DEACCENT;
|
|
pClusterTok->m_Accent_Prom = K_DEACCENT_PROM;
|
|
pClusterTok->POScode = MS_Noun;
|
|
pClusterTok->m_posClass = POS_CONTENT;
|
|
}
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
}
|
|
cWordCount--;
|
|
cWordCount = DoIntegerTemplate( &curPos,
|
|
pMoney->pSecondaryNumberPart,
|
|
cWordCount );
|
|
}
|
|
}
|
|
} /* CFrontend::DoCurrencyTemplate */
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::DoNumByNumTemplate *
|
|
*---------------------------------*
|
|
* Description:
|
|
* Prosody template for RIGHT hand side of the decimal point.
|
|
*
|
|
********************************************************************** MC ***/
|
|
void CFrontend::DoNumByNumTemplate( SPLISTPOS *pClusterPos, long cWordCount )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::DoNumByNumTemplate" );
|
|
CFEToken *pClusterTok;
|
|
SPLISTPOS curPos, nextPos;
|
|
|
|
curPos = nextPos = *pClusterPos;
|
|
while( cWordCount > 1 )
|
|
{
|
|
pClusterTok = NULL;
|
|
//-------------------------------------------------------------
|
|
// Right side of decimal point - add H* to every other word
|
|
//-------------------------------------------------------------
|
|
if( nextPos != NULL )
|
|
{
|
|
curPos = nextPos;
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
}
|
|
cWordCount--;
|
|
|
|
if( pClusterTok )
|
|
{
|
|
pClusterTok->m_Accent = K_ACCENT;
|
|
pClusterTok->m_Accent_Prom = K_ACCENT_PROM;
|
|
pClusterTok->m_AccentSource = ACC_NumByNum;
|
|
}
|
|
if( nextPos != NULL )
|
|
{
|
|
curPos = nextPos;
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
}
|
|
cWordCount--;
|
|
}
|
|
if( cWordCount > 0 )
|
|
{
|
|
if( nextPos != NULL )
|
|
{
|
|
curPos = nextPos;
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
}
|
|
cWordCount--;
|
|
}
|
|
*pClusterPos = nextPos;
|
|
} /* CFrontend::DoNumByNumTemplate */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::DoFractionTemplate *
|
|
*------------------------------*
|
|
* Description:
|
|
* Prosody template for RIGHT side of the decimal point.
|
|
*
|
|
********************************************************************** MC ***/
|
|
long CFrontend::DoFractionTemplate( SPLISTPOS *pClusterPos, TTSNumberItemInfo *pNInfo, long cWordCount )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::DoFractionTemplate" );
|
|
CFEToken *pClusterTok;
|
|
TTSFractionItemInfo *pFInfo;
|
|
CFEToken *pWordTok;
|
|
|
|
pFInfo = pNInfo->pFractionalPart;
|
|
|
|
//--- Do Numerator...
|
|
if ( pFInfo->pNumerator->pIntegerPart )
|
|
{
|
|
cWordCount = DoIntegerTemplate( pClusterPos, pFInfo->pNumerator, cWordCount );
|
|
}
|
|
if( pFInfo->pNumerator->pDecimalPart )
|
|
{
|
|
//-----------------------------------------
|
|
// Skip "point" string...
|
|
//-----------------------------------------
|
|
(void) m_TokList.GetNext( *pClusterPos );
|
|
//-----------------------------------------
|
|
// ...and do single digit prosody
|
|
//-----------------------------------------
|
|
DoNumByNumTemplate( pClusterPos, pFInfo->pNumerator->pDecimalPart->ulNumDigits );
|
|
}
|
|
|
|
//--- Special case - a non-standard fraction (e.g. 1/4)
|
|
if( !pFInfo->fIsStandard )
|
|
{
|
|
if( !*pClusterPos )
|
|
{
|
|
*pClusterPos = m_TokList.GetTailPosition( );
|
|
}
|
|
else
|
|
{
|
|
pClusterTok = m_TokList.GetPrev( *pClusterPos );
|
|
}
|
|
}
|
|
|
|
pWordTok = new CFEToken;
|
|
if( pWordTok )
|
|
{
|
|
*pClusterPos = InsertSilenceBeforePos( pWordTok, *pClusterPos );
|
|
pWordTok->m_SilenceSource = SIL_Fractions_NUM;
|
|
pWordTok->m_TuneBoundaryType = NUMBER_BOUNDARY;
|
|
pWordTok->m_BoundarySource = BND_Frac_Num;
|
|
pWordTok = NULL;
|
|
//----------------------------
|
|
// Skip numerator
|
|
//----------------------------
|
|
if( *pClusterPos != NULL )
|
|
{
|
|
pClusterTok = m_TokList.GetNext( *pClusterPos );
|
|
}
|
|
}
|
|
|
|
//--- Do Denominator...
|
|
if ( pFInfo->pDenominator->pIntegerPart )
|
|
{
|
|
//-----------------------------------------
|
|
// Skip "over" string...
|
|
//-----------------------------------------
|
|
pClusterTok = m_TokList.GetNext( *pClusterPos );
|
|
if( pClusterTok->m_Accent == K_NOACC )
|
|
{
|
|
//--------------------------------------
|
|
// Force POS for "and" to noun
|
|
// so phrasing rules don't kick in!
|
|
//--------------------------------------
|
|
pClusterTok->m_Accent = K_DEACCENT;
|
|
pClusterTok->m_Accent_Prom = K_DEACCENT_PROM;
|
|
pClusterTok->POScode = MS_Noun;
|
|
pClusterTok->m_posClass = POS_CONTENT;
|
|
}
|
|
cWordCount = DoIntegerTemplate( pClusterPos, pFInfo->pDenominator, cWordCount );
|
|
}
|
|
if( pFInfo->pDenominator->pDecimalPart )
|
|
{
|
|
//-----------------------------------------
|
|
// Skip "point" string...
|
|
//-----------------------------------------
|
|
(void) m_TokList.GetNext( *pClusterPos );
|
|
//-----------------------------------------
|
|
// ...and do single digit prosody
|
|
//-----------------------------------------
|
|
DoNumByNumTemplate( pClusterPos, pFInfo->pDenominator->pDecimalPart->ulNumDigits );
|
|
}
|
|
|
|
return cWordCount;
|
|
} /* CFrontend::DoFractionTemplate */
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::DoIntegerTemplate *
|
|
*------------------------------*
|
|
* Description:
|
|
* Prosody template for LEFT hand side of the decimal point.
|
|
*
|
|
********************************************************************** MC ***/
|
|
long CFrontend::DoIntegerTemplate( SPLISTPOS *pClusterPos, TTSNumberItemInfo *pNInfo, long cWordCount )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::DoIntegerTemplate" );
|
|
long i;
|
|
CFEToken *pClusterTok;
|
|
CFEToken *pWordTok = NULL;
|
|
SPLISTPOS curPos, nextPos;
|
|
|
|
//------------------------------------------
|
|
// Special currency hack...sorry
|
|
//------------------------------------------
|
|
if( pNInfo->pIntegerPart->fDigitByDigit )
|
|
{
|
|
DoNumByNumTemplate( pClusterPos, pNInfo->pIntegerPart->ulNumDigits );
|
|
return cWordCount - pNInfo->pIntegerPart->ulNumDigits;
|
|
}
|
|
|
|
nextPos = curPos = *pClusterPos;
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
pClusterTok->m_Accent = K_DEACCENT;
|
|
pClusterTok->m_Accent_Prom = K_DEACCENT_PROM;
|
|
if( pNInfo->fNegative )
|
|
{
|
|
//---------------------------------
|
|
// Skip "NEGATIVE"
|
|
//---------------------------------
|
|
if( nextPos != NULL )
|
|
{
|
|
curPos = nextPos;
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
pClusterTok->m_Accent = K_DEACCENT;
|
|
pClusterTok->m_Accent_Prom = K_DEACCENT_PROM;
|
|
}
|
|
cWordCount--;
|
|
}
|
|
for( i = (pNInfo->pIntegerPart->lNumGroups -1); i >= 0; i-- )
|
|
{
|
|
//------------------------------------
|
|
// Accent 1st digit in group
|
|
//------------------------------------
|
|
pClusterTok->m_Accent = K_ACCENT;
|
|
pClusterTok->m_Accent_Prom = K_ACCENT_PROM;
|
|
pClusterTok->m_AccentSource = ACC_IntegerGroup;
|
|
|
|
|
|
if( pNInfo->pIntegerPart->Groups[i].fHundreds )
|
|
{
|
|
//---------------------------------
|
|
// Skip "X HUNDRED"
|
|
//---------------------------------
|
|
if( nextPos != NULL )
|
|
{
|
|
curPos = nextPos;
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
if( pClusterTok->m_Accent == K_NOACC )
|
|
{
|
|
pClusterTok->m_Accent = K_DEACCENT;
|
|
pClusterTok->m_Accent_Prom = K_DEACCENT_PROM;
|
|
}
|
|
}
|
|
cWordCount--;
|
|
if( nextPos != NULL )
|
|
{
|
|
curPos = nextPos;
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
if( pClusterTok->m_Accent == K_NOACC )
|
|
{
|
|
pClusterTok->m_Accent = K_DEACCENT;
|
|
pClusterTok->m_Accent_Prom = K_DEACCENT_PROM;
|
|
}
|
|
}
|
|
cWordCount--;
|
|
}
|
|
if( pNInfo->pIntegerPart->Groups[i].fTens )
|
|
{
|
|
//---------------------------------
|
|
// Skip "X-TY"
|
|
//---------------------------------
|
|
if( nextPos != NULL )
|
|
{
|
|
curPos = nextPos;
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
if( pClusterTok->m_Accent == K_NOACC )
|
|
{
|
|
pClusterTok->m_Accent = K_DEACCENT;
|
|
pClusterTok->m_Accent_Prom = K_DEACCENT_PROM;
|
|
}
|
|
}
|
|
cWordCount--;
|
|
}
|
|
if( pNInfo->pIntegerPart->Groups[i].fOnes )
|
|
{
|
|
//---------------------------------
|
|
// Skip "X"
|
|
//---------------------------------
|
|
if( nextPos != NULL )
|
|
{
|
|
curPos = nextPos;
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
if( pClusterTok->m_Accent == K_NOACC )
|
|
{
|
|
pClusterTok->m_Accent = K_DEACCENT;
|
|
pClusterTok->m_Accent_Prom = K_DEACCENT_PROM;
|
|
}
|
|
}
|
|
cWordCount--;
|
|
}
|
|
if( pNInfo->pIntegerPart->Groups[i].fQuantifier )
|
|
{
|
|
//---------------------------------
|
|
// Insert SILENCE after quant
|
|
//---------------------------------
|
|
if( pWordTok == NULL )
|
|
{
|
|
pWordTok = new CFEToken;
|
|
}
|
|
if( pWordTok )
|
|
{
|
|
nextPos = InsertSilenceAfterPos( pWordTok, curPos );
|
|
pWordTok->m_SilenceSource = SIL_Integer_Quant;
|
|
pWordTok->m_TuneBoundaryType = NUMBER_BOUNDARY;
|
|
pWordTok->m_BoundarySource = BND_IntegerQuant;
|
|
pWordTok = NULL;
|
|
if( pClusterTok->m_Accent == K_NOACC )
|
|
{
|
|
pClusterTok->m_Accent = K_DEACCENT;
|
|
pClusterTok->m_Accent_Prom = K_DEACCENT_PROM;
|
|
}
|
|
if( nextPos != NULL )
|
|
{
|
|
//------------------------------
|
|
// Skip inserted silence
|
|
//------------------------------
|
|
curPos = nextPos;
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
}
|
|
if( nextPos != NULL )
|
|
{
|
|
//-----------------------------------
|
|
// Skip quantifier string
|
|
//-----------------------------------
|
|
curPos = nextPos;
|
|
pClusterTok = m_TokList.GetNext( nextPos );
|
|
}
|
|
cWordCount--;
|
|
}
|
|
}
|
|
}
|
|
|
|
*pClusterPos = curPos;
|
|
return cWordCount;
|
|
} /* CFrontend::DoIntegerTemplate */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::GetSentenceTokens *
|
|
*------------------------------*
|
|
* Description:
|
|
* Collect Senence Enum tokens.
|
|
* Copy tokens into 'm_TokList' and token count into 'm_cNumOfWords'
|
|
* S_FALSE return means no more input sentences.+++
|
|
*
|
|
********************************************************************** MC ***/
|
|
HRESULT CFrontend::GetSentenceTokens( DIRECTION eDirection )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::GetSentenceTokens" );
|
|
HRESULT eHR = S_OK;
|
|
bool fLastItem = false;
|
|
IEnumSENTITEM *pItemizer;
|
|
TTSSentItem sentItem;
|
|
long tokenIndex;
|
|
CFEToken *pWordTok;
|
|
bool lastWasTerm = false;
|
|
bool lastWasSil = true;
|
|
TUNE_TYPE defaultTune = PHRASE_BOUNDARY;
|
|
long cNumOfItems, cCurItem, cCurWord;
|
|
SPLISTPOS clusterPos, tempPos;
|
|
|
|
m_cNumOfWords = 0;
|
|
pWordTok = NULL;
|
|
clusterPos = NULL;
|
|
|
|
if ( eDirection == eNEXT )
|
|
{
|
|
eHR = m_pEnumSent->Next( &pItemizer );
|
|
}
|
|
else
|
|
{
|
|
eHR = m_pEnumSent->Previous( &pItemizer );
|
|
}
|
|
|
|
|
|
if( eHR == S_OK )
|
|
{
|
|
//--------------------------------------------
|
|
// There's still another sentence to speak
|
|
//--------------------------------------------
|
|
tokenIndex = 0;
|
|
|
|
CItemList& ItemList = ((CSentItemEnum*)pItemizer)->_GetList();
|
|
cNumOfItems = (ItemList.GetCount()) -1;
|
|
cCurItem = 0;
|
|
|
|
//------------------------------------
|
|
// Collect all sentence tokens
|
|
//------------------------------------
|
|
while( (eHR = pItemizer->Next( &sentItem )) == S_OK )
|
|
{
|
|
clusterPos = NULL;
|
|
cCurWord = sentItem.ulNumWords;
|
|
for ( ULONG i = 0; i < sentItem.ulNumWords; i++ )
|
|
{
|
|
//------------------------------
|
|
// Always have a working token
|
|
//------------------------------
|
|
if( pWordTok == NULL )
|
|
{
|
|
pWordTok = new CFEToken;
|
|
}
|
|
if( pWordTok )
|
|
{
|
|
|
|
if( sentItem.pItemInfo->Type & eWORDLIST_IS_VALID )
|
|
{
|
|
//------------------------------------------
|
|
// Get tag values (vol, rate, pitch, etc.)
|
|
//------------------------------------------
|
|
GetItemControls( sentItem.Words[i].pXmlState, pWordTok );
|
|
|
|
//------------------------------------------
|
|
//
|
|
//------------------------------------------
|
|
|
|
//-------------------------------------
|
|
// Switch on token type
|
|
//-------------------------------------
|
|
switch ( sentItem.Words[i].pXmlState->eAction )
|
|
{
|
|
case SPVA_Speak:
|
|
case SPVA_SpellOut:
|
|
{
|
|
//----------------------------------
|
|
// Speak this token
|
|
//----------------------------------
|
|
pWordTok->tokLen = sentItem.Words[i].ulWordLen;
|
|
if( pWordTok->tokLen > (TOKEN_LEN_MAX -1) )
|
|
{
|
|
//-----------------------------------
|
|
// Clip to max string length
|
|
//-----------------------------------
|
|
pWordTok->tokLen = TOKEN_LEN_MAX -1;
|
|
}
|
|
//--------------------------
|
|
// Copy token string
|
|
// Append C-string delimiter
|
|
//--------------------------
|
|
memcpy( &pWordTok->tokStr[0], &sentItem.Words[i].pWordText[0],
|
|
pWordTok->tokLen * sizeof(WCHAR) );
|
|
pWordTok->tokStr[pWordTok->tokLen] = 0; //string delimiter
|
|
|
|
pWordTok->phon_Len = IPA_to_Allo( sentItem.Words[i].pWordPron,
|
|
pWordTok->phon_Str );
|
|
pWordTok->POScode = sentItem.Words[i].eWordPartOfSpeech;
|
|
pWordTok->m_posClass = GetPOSClass( pWordTok->POScode );
|
|
pWordTok->srcPosition = sentItem.ulItemSrcOffset;
|
|
pWordTok->srcLen = sentItem.ulItemSrcLen;
|
|
pWordTok->m_PitchBaseOffs = m_CurPitchOffs;
|
|
pWordTok->m_PitchRangeScale = m_CurPitchRange;
|
|
pWordTok->m_ProsodyDurScale = m_RateRatio_PROSODY;
|
|
|
|
//----------------------------------
|
|
// Advance to next token
|
|
//----------------------------------
|
|
tempPos = m_TokList.AddTail( pWordTok );
|
|
if( clusterPos == NULL )
|
|
{
|
|
//--------------------------------------
|
|
// Remember where currentitem started
|
|
//--------------------------------------
|
|
clusterPos = tempPos;
|
|
}
|
|
pWordTok = NULL; // Get a new ptr next time
|
|
tokenIndex++;
|
|
lastWasTerm = false;
|
|
lastWasSil = false;
|
|
|
|
break;
|
|
}
|
|
|
|
case SPVA_Silence:
|
|
{
|
|
(void)InsertSilenceAtTail( pWordTok, &sentItem, sentItem.Words[i].pXmlState->SilenceMSecs );
|
|
pWordTok->m_SilenceSource = SIL_XML;
|
|
pWordTok = NULL; // Get a new ptr next time
|
|
tokenIndex++;
|
|
lastWasTerm = false;
|
|
break;
|
|
}
|
|
|
|
case SPVA_Pronounce:
|
|
{
|
|
pWordTok->tokStr[0] = 0; // There's no orth for Pron types
|
|
pWordTok->tokLen = 0;
|
|
pWordTok->phon_Len = IPA_to_Allo( sentItem.Words[i].pXmlState->pPhoneIds, pWordTok->phon_Str );
|
|
pWordTok->POScode = sentItem.Words[i].eWordPartOfSpeech;
|
|
pWordTok->m_posClass = GetPOSClass( pWordTok->POScode );
|
|
pWordTok->srcPosition = sentItem.ulItemSrcOffset;
|
|
pWordTok->srcLen = sentItem.ulItemSrcLen;
|
|
pWordTok->m_PitchBaseOffs = m_CurPitchOffs;
|
|
pWordTok->m_PitchRangeScale = m_CurPitchRange;
|
|
pWordTok->m_ProsodyDurScale = m_RateRatio_PROSODY;
|
|
|
|
//----------------------------------
|
|
// Advance to next token
|
|
//----------------------------------
|
|
tempPos = m_TokList.AddTail( pWordTok );
|
|
if( clusterPos == NULL )
|
|
{
|
|
//--------------------------------------
|
|
// Remember where currentitem started
|
|
//--------------------------------------
|
|
clusterPos = tempPos;
|
|
}
|
|
pWordTok = NULL; // Get a new ptr next time
|
|
tokenIndex++;
|
|
lastWasTerm = false;
|
|
lastWasSil = false;
|
|
break;
|
|
}
|
|
|
|
case SPVA_Bookmark:
|
|
{
|
|
BOOKMARK_ITEM *pMarker;
|
|
//-------------------------------------------------
|
|
// Create bookmark list if it's not already there
|
|
//-------------------------------------------------
|
|
if( pWordTok->pBMObj == NULL )
|
|
{
|
|
pWordTok->pBMObj = new CBookmarkList;
|
|
}
|
|
if( pWordTok->pBMObj )
|
|
{
|
|
//--------------------------------------------------------
|
|
// Allocate memory for bookmark string
|
|
// (add 1 to length for string delimiter)
|
|
//--------------------------------------------------------
|
|
pWordTok->tokLen = sentItem.Words[i].ulWordLen;
|
|
pMarker = new BOOKMARK_ITEM;
|
|
if (pMarker)
|
|
{
|
|
//----------------------------------------
|
|
// We'll need the text ptr and length
|
|
// when this bookmark event gets posted
|
|
//----------------------------------------
|
|
pMarker->pBMItem = (LPARAM)sentItem.pItemSrcText;
|
|
//--- Punch NULL character into end of bookmark string for Event...
|
|
WCHAR* pTemp = (WCHAR*) sentItem.pItemSrcText + sentItem.ulItemSrcLen;
|
|
*pTemp = 0;
|
|
|
|
//-----------------------------------
|
|
// Add this bookmark to list
|
|
//-----------------------------------
|
|
pWordTok->pBMObj->m_BMList.AddTail( pMarker );
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
SPDBG_DMSG1( "Unknown SPVSTATE eAction: %d\n", sentItem.Words[i].pXmlState->eAction );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//-----------------------------
|
|
// Maybe token is punctuation
|
|
//-----------------------------
|
|
if ( fIsPunctuation(sentItem) )
|
|
{
|
|
TUNE_TYPE bType = NULL_BOUNDARY;
|
|
|
|
switch ( sentItem.pItemInfo->Type )
|
|
{
|
|
case eCOMMA:
|
|
case eSEMICOLON:
|
|
case eCOLON:
|
|
if( !lastWasSil )
|
|
{
|
|
bType = PHRASE_BOUNDARY;
|
|
}
|
|
break;
|
|
case ePERIOD:
|
|
if( fLastItem )
|
|
{
|
|
bType = DECLAR_BOUNDARY;
|
|
}
|
|
else
|
|
{
|
|
defaultTune = DECLAR_BOUNDARY;
|
|
}
|
|
break;
|
|
case eQUESTION:
|
|
if( fLastItem )
|
|
{
|
|
bType = YN_QUEST_BOUNDARY;
|
|
}
|
|
else
|
|
{
|
|
defaultTune = YN_QUEST_BOUNDARY;
|
|
}
|
|
break;
|
|
case eEXCLAMATION:
|
|
if( fLastItem )
|
|
{
|
|
bType = EXCLAM_BOUNDARY;
|
|
}
|
|
else
|
|
{
|
|
defaultTune = EXCLAM_BOUNDARY;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if( (bType != NULL_BOUNDARY) && (tokenIndex > 0) )
|
|
{
|
|
pWordTok->m_TuneBoundaryType = bType;
|
|
|
|
pWordTok->phon_Len = 1;
|
|
pWordTok->phon_Str[0] = _SIL_;
|
|
pWordTok->srcPosition = sentItem.ulItemSrcOffset;
|
|
pWordTok->srcLen = sentItem.ulItemSrcLen;
|
|
pWordTok->tokStr[0] = sentItem.pItemSrcText[0]; // punctuation
|
|
pWordTok->tokStr[1] = 0; // delimiter
|
|
pWordTok->tokLen = 1;
|
|
pWordTok->m_SilenceSource = SIL_Term;
|
|
pWordTok->m_TermSil = 0;
|
|
//----------------------------------
|
|
// Advance to next token
|
|
//----------------------------------
|
|
tempPos = m_TokList.AddTail( pWordTok );
|
|
if( clusterPos == NULL )
|
|
{
|
|
//--------------------------------------
|
|
// Remember where currentitem started
|
|
//--------------------------------------
|
|
clusterPos = tempPos;
|
|
}
|
|
pWordTok = NULL; // Get a new ptr next time
|
|
tokenIndex++;
|
|
lastWasTerm = true;
|
|
lastWasSil = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch ( sentItem.pItemInfo->Type )
|
|
{
|
|
//case eSINGLE_QUOTE:
|
|
case eDOUBLE_QUOTE:
|
|
if( StateQuoteProsody( pWordTok, &sentItem, (!fLastItem) & (!lastWasSil) ) )
|
|
{
|
|
if( (!fLastItem) & (!lastWasSil) )
|
|
{
|
|
pWordTok = NULL; // Get a new ptr next time
|
|
tokenIndex++;
|
|
}
|
|
lastWasTerm = false;
|
|
lastWasSil = true;
|
|
}
|
|
break;
|
|
|
|
case eOPEN_PARENTHESIS:
|
|
case eOPEN_BRACKET:
|
|
case eOPEN_BRACE:
|
|
if( StartParenProsody( pWordTok, &sentItem, !fLastItem ) )
|
|
{
|
|
if( !fLastItem )
|
|
{
|
|
pWordTok = NULL; // Get a new ptr next time
|
|
tokenIndex++;
|
|
}
|
|
lastWasTerm = false;
|
|
lastWasSil = true;
|
|
}
|
|
break;
|
|
|
|
case eCLOSE_PARENTHESIS:
|
|
case eCLOSE_BRACKET:
|
|
case eCLOSE_BRACE:
|
|
if( EndParenProsody( pWordTok, &sentItem, !fLastItem ) )
|
|
{
|
|
if( !fLastItem )
|
|
{
|
|
pWordTok = NULL; // Get a new ptr next time
|
|
tokenIndex++;
|
|
}
|
|
lastWasTerm = false;
|
|
lastWasSil = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
eHR = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
if( --cCurWord == 0 )
|
|
{
|
|
cCurItem++;
|
|
}
|
|
if( cCurItem == cNumOfItems )
|
|
{
|
|
fLastItem = true;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------
|
|
// Tag special word clusters
|
|
//-------------------------------------
|
|
ProsodyTemplates( clusterPos, &sentItem );
|
|
|
|
}
|
|
|
|
pItemizer->Release();
|
|
|
|
//------------------------------------------------------
|
|
// Make sure sentence ends on termination
|
|
//------------------------------------------------------
|
|
if( !lastWasTerm )
|
|
{
|
|
//------------------------
|
|
// Add a comma
|
|
//------------------------
|
|
if( pWordTok == NULL )
|
|
{
|
|
pWordTok = new CFEToken;
|
|
}
|
|
if( pWordTok )
|
|
{
|
|
pWordTok->m_TuneBoundaryType = defaultTune;
|
|
pWordTok->m_BoundarySource = BND_ForcedTerm;
|
|
pWordTok->m_SilenceSource = SIL_Term;
|
|
pWordTok->phon_Len = 1;
|
|
pWordTok->phon_Str[0] = _SIL_;
|
|
pWordTok->srcPosition = sentItem.ulItemSrcOffset;
|
|
pWordTok->srcLen = sentItem.ulItemSrcLen;
|
|
pWordTok->tokStr[0] = '.'; // punctuation
|
|
pWordTok->tokStr[1] = 0; // delimiter
|
|
pWordTok->tokLen = 1;
|
|
// pWordTok->m_BoundarySource = bndSource;
|
|
//----------------------------------
|
|
// Advance to next token
|
|
//----------------------------------
|
|
tempPos = m_TokList.AddTail( pWordTok );
|
|
if( clusterPos == NULL )
|
|
{
|
|
//--------------------------------------
|
|
// Remember where current item started
|
|
//--------------------------------------
|
|
clusterPos = tempPos;
|
|
}
|
|
pWordTok = NULL; // Get a new ptr next time
|
|
tokenIndex++;
|
|
}
|
|
else
|
|
{
|
|
//----------------------------------
|
|
// Bail-out or we'll crash
|
|
//----------------------------------
|
|
eHR = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
m_cNumOfWords = tokenIndex;
|
|
if( eHR == S_FALSE )
|
|
{
|
|
//----------------------------------
|
|
// Return only errors
|
|
//----------------------------------
|
|
eHR = S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
eHR = eHR; // !!!!
|
|
}
|
|
|
|
//-------------------------------
|
|
// Cleanup memory allocation
|
|
//-------------------------------
|
|
if( pWordTok != NULL )
|
|
{
|
|
delete pWordTok;
|
|
}
|
|
|
|
//---------------------------------------------------
|
|
// Get sentence position and length for SAPI events
|
|
//---------------------------------------------------
|
|
CalcSentenceLength();
|
|
|
|
return eHR;
|
|
} /* CFrontend::GetSentenceTokens */
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::CalcSentenceLength *
|
|
*-------------------------------*
|
|
* Description:
|
|
* Loop thru token list and sum the source char count.
|
|
*
|
|
********************************************************************** MC ***/
|
|
void CFrontend::CalcSentenceLength()
|
|
{
|
|
long firstIndex, lastIndex, lastLen;
|
|
bool firstState;
|
|
SPLISTPOS listPos;
|
|
CFEToken *pWordTok, *pFirstTok = NULL;
|
|
|
|
//---------------------------------------------
|
|
// Find the 1st and last words in sentence
|
|
//---------------------------------------------
|
|
firstIndex = lastIndex = lastLen = 0;
|
|
firstState = true;
|
|
listPos = m_TokList.GetHeadPosition();
|
|
while( listPos )
|
|
{
|
|
pWordTok = m_TokList.GetNext( listPos );
|
|
//-------------------------------------------
|
|
// Look at at displayable words only
|
|
//-------------------------------------------
|
|
if( pWordTok->srcLen > 0 )
|
|
{
|
|
if( firstState )
|
|
{
|
|
firstState = false;
|
|
firstIndex = pWordTok->srcPosition;
|
|
pFirstTok = pWordTok;
|
|
}
|
|
else
|
|
{
|
|
lastIndex = pWordTok->srcPosition;
|
|
lastLen = pWordTok->srcLen;
|
|
}
|
|
}
|
|
}
|
|
//--------------------------------------------------
|
|
// Calculate sentence length for head list item
|
|
//--------------------------------------------------
|
|
if( pFirstTok )
|
|
{
|
|
pFirstTok->sentencePosition = firstIndex; // Sentence starts here...
|
|
pFirstTok->sentenceLen = (lastIndex - firstIndex) + lastLen; // ...and this is the length
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::DisposeUnits *
|
|
*-------------------------*
|
|
* Description:
|
|
* Delete memory allocated to 'm_pUnits'.
|
|
* Clean-up memory for Bookmarks
|
|
*
|
|
********************************************************************** MC ***/
|
|
void CFrontend::DisposeUnits( )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::DisposeUnits" );
|
|
ULONG unitIndex;
|
|
|
|
if( m_pUnits )
|
|
{
|
|
//-----------------------------------------
|
|
// Clean-up Bookmark memory allocation
|
|
//-----------------------------------------
|
|
|
|
for( unitIndex = m_CurUnitIndex; unitIndex < m_unitCount; unitIndex++)
|
|
{
|
|
if( m_pUnits[unitIndex].pBMObj != NULL )
|
|
{
|
|
//---------------------------------------
|
|
// Dispose bookmark list
|
|
//---------------------------------------
|
|
delete m_pUnits[unitIndex].pBMObj;
|
|
m_pUnits[unitIndex].pBMObj = NULL;
|
|
}
|
|
}
|
|
delete m_pUnits;
|
|
m_pUnits = NULL;
|
|
}
|
|
} /* CFrontend::DisposeUnits */
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::ParseNextSentence *
|
|
*------------------------------*
|
|
* Description:
|
|
* Fill 'm_pUnits' array with next sentence.
|
|
* If there's no more input text,
|
|
* return with 'm_SpeechState' set to SPEECH_DONE +++
|
|
*
|
|
********************************************************************** MC ***/
|
|
HRESULT CFrontend::ParseSentence( DIRECTION eDirection )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::ParseNextSentence" );
|
|
HRESULT hr = S_OK;
|
|
|
|
//-----------------------------------------------------
|
|
// If there's a previous unit array, free its memory
|
|
//-----------------------------------------------------
|
|
DisposeUnits();
|
|
m_CurUnitIndex = 0;
|
|
m_unitCount = 0;
|
|
DeleteTokenList();
|
|
m_pUnits = NULL;
|
|
//-----------------------------------------------------
|
|
// If there's a previous allo array, free its memory
|
|
//-----------------------------------------------------
|
|
if( m_pAllos )
|
|
{
|
|
delete m_pAllos;
|
|
m_pAllos = NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
// Fill token array with next sentence
|
|
// Skip empty sentences.
|
|
// NOTE: includes non-speaking items
|
|
//-----------------------------------------------------
|
|
do
|
|
{
|
|
hr = GetSentenceTokens( eDirection );
|
|
} while( (hr == S_OK) && (m_cNumOfWords == 0) );
|
|
|
|
if( hr == S_OK )
|
|
{
|
|
//--------------------------------------------
|
|
// Prepare word emphasis
|
|
//--------------------------------------------
|
|
DoWordAccent();
|
|
|
|
//--------------------------------------------
|
|
// Word level prosodic lables
|
|
//--------------------------------------------
|
|
DoPhrasing();
|
|
ToBISymbols();
|
|
|
|
//--------------------------------------------
|
|
// Convert tokens to allo list
|
|
//--------------------------------------------
|
|
m_pAllos = new CAlloList;
|
|
if (m_pAllos == NULL)
|
|
{
|
|
//-----------------------
|
|
// Out of memory
|
|
//-----------------------
|
|
hr = E_FAIL;
|
|
}
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
//--------------------------------
|
|
// Convert word to allo strteam
|
|
//-------------------------------
|
|
TokensToAllo( &m_TokList, m_pAllos );
|
|
|
|
//----------------------------
|
|
// Tag sentence syllables
|
|
//----------------------------
|
|
m_SyllObj.TagSyllables( m_pAllos );
|
|
|
|
//--------------------------------------------
|
|
// Dispose token array, no longer needed
|
|
//--------------------------------------------
|
|
DeleteTokenList();
|
|
|
|
//--------------------------------------------
|
|
// Create the unit array
|
|
// NOTE:
|
|
//--------------------------------------------
|
|
hr = UnitLookahead ();
|
|
if( hr == S_OK )
|
|
{
|
|
//--------------------------------------------
|
|
// Compute allo durations
|
|
//--------------------------------------------
|
|
UnitToAlloDur( m_pAllos, m_pUnits );
|
|
m_DurObj.AlloDuration( m_pAllos, m_RateRatio_API );
|
|
//--------------------------------------------
|
|
// Modulate allo pitch
|
|
//--------------------------------------------
|
|
m_PitchObj.AlloPitch( m_pAllos, m_BasePitch, m_PitchRange );
|
|
}
|
|
|
|
|
|
}
|
|
if( hr == S_OK )
|
|
{
|
|
AlloToUnitPitch( m_pAllos, m_pUnits );
|
|
}
|
|
}
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
//------------------------------------------
|
|
// Either the input text is dry or we failed.
|
|
// Try to fail gracefully
|
|
// 1 - Clean up memory
|
|
// 2 - End the speech
|
|
//------------------------------------------
|
|
if( m_pAllos )
|
|
{
|
|
delete m_pAllos;
|
|
m_pAllos = 0;
|
|
}
|
|
DeleteTokenList();
|
|
DisposeUnits();
|
|
m_SpeechState = SPEECH_DONE;
|
|
}
|
|
else if( hr == S_FALSE )
|
|
{
|
|
//---------------------------------
|
|
// No more input text
|
|
//---------------------------------
|
|
hr = S_OK;
|
|
m_SpeechState = SPEECH_DONE;
|
|
}
|
|
|
|
|
|
return hr;
|
|
} /* CFrontend::ParseNextSentence */
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::UnitLookahead *
|
|
*--------------------------*
|
|
* Description:
|
|
*
|
|
********************************************************************** MC ***/
|
|
HRESULT CFrontend::UnitLookahead ()
|
|
{
|
|
SPDBG_FUNC( "CFrontend::UnitLookahead" );
|
|
HRESULT hr = S_OK;
|
|
UNIT_CVT *pPhon2Unit = NULL;
|
|
ULONG i;
|
|
|
|
m_unitCount = m_pAllos->GetCount();
|
|
|
|
m_pUnits = new UNITINFO[m_unitCount];
|
|
if( m_pUnits )
|
|
{
|
|
pPhon2Unit = new UNIT_CVT[m_unitCount];
|
|
if( pPhon2Unit )
|
|
{
|
|
//--------------------------------------------
|
|
// Convert allo list to unit array
|
|
//--------------------------------------------
|
|
memset( m_pUnits, 0, m_unitCount * sizeof(UNITINFO) );
|
|
hr = AlloToUnit( m_pAllos, m_pUnits );
|
|
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
//--------------------------------------------
|
|
// Initialize UNIT_CVT
|
|
//--------------------------------------------
|
|
for( i = 0; i < m_unitCount; i++ )
|
|
{
|
|
pPhon2Unit[i].PhonID = m_pUnits[i].PhonID;
|
|
pPhon2Unit[i].flags = m_pUnits[i].flags;
|
|
}
|
|
//--------------------------------------------
|
|
// Compute triphone IDs
|
|
//--------------------------------------------
|
|
hr = m_pVoiceDataObj->GetUnitIDs( pPhon2Unit, m_unitCount );
|
|
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
//--------------------------------------------
|
|
// Copy UNIT_CVT to UNITINFO
|
|
//--------------------------------------------
|
|
for( i = 0; i < m_unitCount; i++ )
|
|
{
|
|
m_pUnits[i].UnitID = pPhon2Unit[i].UnitID;
|
|
m_pUnits[i].SenoneID = pPhon2Unit[i].SenoneID;
|
|
m_pUnits[i].duration = pPhon2Unit[i].Dur;
|
|
m_pUnits[i].amp = pPhon2Unit[i].Amp;
|
|
m_pUnits[i].ampRatio = pPhon2Unit[i].AmpRatio;
|
|
strcpy( m_pUnits[i].szUnitName, pPhon2Unit[i].szUnitName );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//-----------------------
|
|
// Can't get unit ID's
|
|
//-----------------------
|
|
delete m_pUnits;
|
|
m_pUnits = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//-----------------------
|
|
// Can't convert allos
|
|
//-----------------------
|
|
delete m_pUnits;
|
|
m_pUnits = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//-----------------------
|
|
// Out of memory
|
|
//-----------------------
|
|
delete m_pUnits;
|
|
m_pUnits = NULL;
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//-----------------------
|
|
// Out of memory
|
|
//-----------------------
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
//------------------------------
|
|
// Cleanup before exit
|
|
//------------------------------
|
|
if( pPhon2Unit )
|
|
{
|
|
delete pPhon2Unit;
|
|
}
|
|
|
|
|
|
return hr;
|
|
} /* CFrontend::UnitLookahead */
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::UnitToAlloDur *
|
|
*--------------------------*
|
|
* Description:
|
|
*
|
|
********************************************************************** MC ***/
|
|
void CFrontend::UnitToAlloDur( CAlloList *pAllos, UNITINFO *pu )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::UnitToAlloDur" );
|
|
CAlloCell *pCurCell;
|
|
|
|
pCurCell = pAllos->GetHeadCell();
|
|
while( pCurCell )
|
|
{
|
|
pCurCell->m_UnitDur = pu->duration;
|
|
pu++;
|
|
pCurCell = pAllos->GetNextCell();
|
|
}
|
|
} /* CFrontend::UnitToAlloDur */
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::AlloToUnitPitch *
|
|
*----------------------------*
|
|
* Description:
|
|
*
|
|
********************************************************************** MC ***/
|
|
void CFrontend::AlloToUnitPitch( CAlloList *pAllos, UNITINFO *pu )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::AlloToUnitPitch" );
|
|
ULONG k;
|
|
CAlloCell *pCurCell;
|
|
|
|
pCurCell = pAllos->GetHeadCell();
|
|
while( pCurCell )
|
|
{
|
|
pu->duration = pCurCell->m_ftDuration;
|
|
for( k = 0; k < pu->nKnots; k++ )
|
|
{
|
|
pu->pTime[k] = pCurCell->m_ftTime[k] * m_SampleRate;
|
|
pu->pF0[k] = pCurCell->m_ftPitch[k];
|
|
pu->pAmp[k] = pu->ampRatio;
|
|
}
|
|
pu++;
|
|
pCurCell = pAllos->GetNextCell();
|
|
}
|
|
} /* CFrontend::AlloToUnitPitch */
|
|
|
|
|
|
/*****************************************************************************
|
|
* CAlloList::DeleteTokenList *
|
|
*----------------------------*
|
|
* Description:
|
|
* Remove every item in link list.
|
|
*
|
|
********************************************************************** MC ***/
|
|
void CFrontend::DeleteTokenList()
|
|
{
|
|
SPDBG_FUNC( "CFrontend::DeleteTokenList" );
|
|
CFEToken *pTok;
|
|
|
|
while( !m_TokList.IsEmpty() )
|
|
{
|
|
pTok = (CFEToken*)m_TokList.RemoveHead();
|
|
delete pTok;
|
|
}
|
|
|
|
} /* CFrontend::DeleteTokenList */
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* AdjustQuestTune *
|
|
*-----------------*
|
|
* Description:
|
|
* Adjust termination for either YN or WH sentence tune.
|
|
*
|
|
********************************************************************** MC ***/
|
|
static void AdjustQuestTune( CFEToken *pTok, bool fIsYesNo )
|
|
{
|
|
SPDBG_FUNC( "AdjustQuestTune" );
|
|
if ( pTok->m_TuneBoundaryType > NULL_BOUNDARY )
|
|
{
|
|
if( (pTok->m_TuneBoundaryType == YN_QUEST_BOUNDARY) ||
|
|
(pTok->m_TuneBoundaryType == WH_QUEST_BOUNDARY) )
|
|
{
|
|
//------------------------------------
|
|
// Is this a yes/no question phrase
|
|
//------------------------------------
|
|
if( fIsYesNo )
|
|
{
|
|
//------------------------------------------
|
|
// Put out a final yes/no question marker
|
|
//------------------------------------------
|
|
pTok->m_TuneBoundaryType = YN_QUEST_BOUNDARY;
|
|
pTok->m_BoundarySource = BND_YNQuest;
|
|
}
|
|
else
|
|
{
|
|
|
|
//------------------------------------------------------------------------
|
|
// Use declarative phrase marker (for WH questions)
|
|
//------------------------------------------------------------------------
|
|
pTok->m_TuneBoundaryType = WH_QUEST_BOUNDARY;
|
|
pTok->m_BoundarySource = BND_WHQuest;
|
|
}
|
|
}
|
|
}
|
|
} /* AdjustQuestTune */
|
|
|
|
|
|
typedef enum
|
|
{
|
|
p_Interj,
|
|
P_Adv,
|
|
P_Verb,
|
|
P_Adj,
|
|
P_Noun,
|
|
PRIORITY_SIZE,
|
|
} CONTENT_PRIORITY;
|
|
|
|
#define NO_POSITION -1
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::ExclamEmph *
|
|
*-----------------------*
|
|
* Description:
|
|
* Find a likely word to emph if sentence has exclamation
|
|
*
|
|
********************************************************************** MC ***/
|
|
void CFrontend::ExclamEmph()
|
|
{
|
|
SPDBG_FUNC( "CFrontend::ExclamEmph" );
|
|
CFEToken *pCur_Tok;
|
|
SPLISTPOS listPos, targetPos, curPos, contentPos[PRIORITY_SIZE];
|
|
long cContent, cWords;
|
|
long i;
|
|
|
|
for(i = 0; i < PRIORITY_SIZE; i++ )
|
|
{
|
|
contentPos[i] = (SPLISTPOS)NO_POSITION;
|
|
}
|
|
|
|
listPos = m_TokList.GetTailPosition();
|
|
pCur_Tok = m_TokList.GetNext( listPos );
|
|
|
|
//---------------------------------------------------
|
|
// First, check last token fors an exclamation
|
|
//---------------------------------------------------
|
|
if( pCur_Tok->m_TuneBoundaryType == EXCLAM_BOUNDARY )
|
|
{
|
|
//-----------------------------------------------------
|
|
// Then, see if there's only one content word
|
|
// in the sentence
|
|
//-----------------------------------------------------
|
|
cContent = cWords = 0;
|
|
listPos = m_TokList.GetHeadPosition();
|
|
while( listPos )
|
|
{
|
|
curPos = listPos;
|
|
pCur_Tok = m_TokList.GetNext( listPos );
|
|
if( pCur_Tok->m_posClass == POS_CONTENT )
|
|
{
|
|
cContent++;
|
|
cWords++;
|
|
if( cContent == 1)
|
|
{
|
|
targetPos = curPos;
|
|
}
|
|
//--------------------------------------------------------
|
|
// Fill the famous Azara Content Prominence Hierarchy (ACPH)
|
|
//--------------------------------------------------------
|
|
if( (pCur_Tok->POScode == MS_Noun) && (contentPos[P_Noun] == (SPLISTPOS)NO_POSITION) )
|
|
{
|
|
contentPos[P_Noun] = curPos;
|
|
}
|
|
else if( (pCur_Tok->POScode == MS_Verb) && (contentPos[P_Verb] == (SPLISTPOS)NO_POSITION) )
|
|
{
|
|
contentPos[P_Verb] = curPos;
|
|
}
|
|
else if( (pCur_Tok->POScode == MS_Adj) && (contentPos[P_Adj] == (SPLISTPOS)NO_POSITION) )
|
|
{
|
|
contentPos[P_Adj] = curPos;
|
|
}
|
|
else if( (pCur_Tok->POScode == MS_Adv) && (contentPos[P_Adv] == (SPLISTPOS)NO_POSITION) )
|
|
{
|
|
contentPos[P_Adv] = curPos;
|
|
}
|
|
else if( (pCur_Tok->POScode == MS_Interjection) && (contentPos[p_Interj] == (SPLISTPOS)NO_POSITION) )
|
|
{
|
|
contentPos[p_Interj] = curPos;
|
|
}
|
|
}
|
|
else if( pCur_Tok->m_posClass == POS_FUNC )
|
|
{
|
|
cWords++;
|
|
if( cWords == 1)
|
|
{
|
|
targetPos = curPos;
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------
|
|
// If there's only one word or content word
|
|
// then EMPHASIZE it
|
|
//--------------------------------------------
|
|
if( (cContent == 1) || (cWords == 1) )
|
|
{
|
|
pCur_Tok = m_TokList.GetNext( targetPos );
|
|
pCur_Tok->user_Emph = 1;
|
|
}
|
|
else if( cContent > 1 )
|
|
{
|
|
for(i = 0; i < PRIORITY_SIZE; i++ )
|
|
{
|
|
if( contentPos[i] != (SPLISTPOS)NO_POSITION )
|
|
{
|
|
targetPos = contentPos[i];
|
|
break;
|
|
}
|
|
}
|
|
pCur_Tok = m_TokList.GetNext( targetPos );
|
|
pCur_Tok->user_Emph = 1;
|
|
}
|
|
}
|
|
} //ExclamEmph
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::DoWordAccent *
|
|
*-------------------------*
|
|
* Description:
|
|
* Prepare word for emphasis
|
|
*
|
|
********************************************************************** MC ***/
|
|
void CFrontend::DoWordAccent()
|
|
{
|
|
SPDBG_FUNC( "CFrontend::DoWordAccent" );
|
|
long cNumOfWords;
|
|
long iCurWord;
|
|
CFEToken *pCur_Tok, *pNext_Tok, *pPrev_Tok, *pTempTok;
|
|
SPLISTPOS listPos;
|
|
TUNE_TYPE cur_Bnd, prev_Bnd;
|
|
|
|
//-----------------------------
|
|
// Initilize locals
|
|
//-----------------------------
|
|
cNumOfWords = m_TokList.GetCount();
|
|
if( cNumOfWords > 0 )
|
|
{
|
|
ExclamEmph();
|
|
prev_Bnd = PHRASE_BOUNDARY; // Assume start of sentence
|
|
//-------------------------------------
|
|
// Fill the token pipeline
|
|
//-------------------------------------
|
|
listPos = m_TokList.GetHeadPosition();
|
|
|
|
//-- Previous
|
|
pPrev_Tok = NULL;
|
|
|
|
//-- Current
|
|
pCur_Tok = m_TokList.GetNext( listPos );
|
|
|
|
//-- Next
|
|
if( listPos )
|
|
{
|
|
pNext_Tok = m_TokList.GetNext( listPos );
|
|
}
|
|
else
|
|
{
|
|
pNext_Tok = NULL;
|
|
}
|
|
|
|
//-----------------------------------
|
|
// Step through entire word array
|
|
// (skip last)
|
|
//-----------------------------------
|
|
for( iCurWord = 0; iCurWord < (cNumOfWords -1); iCurWord++ )
|
|
{
|
|
cur_Bnd = pCur_Tok->m_TuneBoundaryType;
|
|
if( pCur_Tok->user_Emph > 0 )
|
|
{
|
|
//-----------------------------------
|
|
// Current word is emphasized
|
|
//-----------------------------------
|
|
if( prev_Bnd == NULL_BOUNDARY )
|
|
{
|
|
pTempTok = new CFEToken;
|
|
if( pTempTok )
|
|
{
|
|
pTempTok->user_Break = EMPH_HESITATION;
|
|
pTempTok->m_TuneBoundaryType = NULL_BOUNDARY;
|
|
pTempTok->phon_Len = 1;
|
|
pTempTok->phon_Str[0] = _SIL_;
|
|
pTempTok->srcPosition = pCur_Tok->srcPosition;
|
|
pTempTok->srcLen = pCur_Tok->srcLen;
|
|
pTempTok->tokStr[0] = 0; // There's no orth for Break
|
|
pTempTok->tokLen = 0;
|
|
pTempTok->m_TermSil = 0;
|
|
pTempTok->m_SilenceSource = SIL_Emph;
|
|
pTempTok->m_DurScale = 0;
|
|
if( pPrev_Tok )
|
|
{
|
|
//pTempTok->m_DurScale = pPrev_Tok->m_DurScale;
|
|
pTempTok->m_ProsodyDurScale = pPrev_Tok->m_ProsodyDurScale;
|
|
pTempTok->user_Volume = pPrev_Tok->user_Volume;
|
|
}
|
|
else
|
|
{
|
|
//pTempTok->m_DurScale = 1.0f;
|
|
pTempTok->m_ProsodyDurScale = 1.0f;
|
|
}
|
|
|
|
m_TokList.InsertBefore( m_TokList.FindIndex( iCurWord ), pTempTok );
|
|
pCur_Tok = pTempTok;
|
|
m_cNumOfWords++;
|
|
cNumOfWords++;
|
|
iCurWord++;
|
|
}
|
|
}
|
|
}
|
|
//------------------------------
|
|
// Shift the token pipeline
|
|
//------------------------------
|
|
prev_Bnd = cur_Bnd;
|
|
pPrev_Tok = pCur_Tok;
|
|
pCur_Tok = pNext_Tok;
|
|
if( listPos )
|
|
{
|
|
pNext_Tok = m_TokList.GetNext( listPos );
|
|
}
|
|
else
|
|
{
|
|
pNext_Tok = NULL;
|
|
}
|
|
|
|
}
|
|
}
|
|
} /* CFrontend::DoWordAccent */
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::DoPhrasing *
|
|
*-----------------------*
|
|
* Description:
|
|
* Insert sub-phrase boundaries into word token array
|
|
*
|
|
********************************************************************** MC ***/
|
|
void CFrontend::DoPhrasing()
|
|
{
|
|
SPDBG_FUNC( "CFrontend::DoPhrasing" );
|
|
long iCurWord;
|
|
CFEToken *pCur_Tok, *pNext_Tok, *pNext2_Tok, *pNext3_Tok, *pTempTok, *pPrev_Tok;
|
|
ENGPARTOFSPEECH cur_POS, next_POS, next2_POS, next3_POS, prev_POS;
|
|
bool fNext_IsPunct, fNext2_IsPunct, fNext3_IsPunct;
|
|
bool fIsYesNo, fMaybeWH, fHasDet, fInitial_Adv, fIsShortSent, fIsAlphaWH;
|
|
TUNE_TYPE cur_Bnd, prev_Punct;
|
|
long punctDistance;
|
|
long cNumOfWords;
|
|
SPLISTPOS listPos;
|
|
BOUNDARY_SOURCE bndNum;
|
|
ACCENT_SOURCE accNum;
|
|
|
|
//-----------------------------
|
|
// Initialize locals
|
|
//-----------------------------
|
|
cNumOfWords = m_TokList.GetCount();
|
|
if( cNumOfWords > 0 )
|
|
{
|
|
cur_Bnd = NULL_BOUNDARY;
|
|
prev_POS = MS_Unknown;
|
|
prev_Punct = PHRASE_BOUNDARY; // Assume start of sentence
|
|
punctDistance = 0; // To quiet the compiler...
|
|
fIsYesNo = fMaybeWH = fHasDet = fIsAlphaWH = false; // To quiet the compiler...
|
|
fMaybeWH = false;
|
|
fInitial_Adv = false;
|
|
if (cNumOfWords <= 9)
|
|
{
|
|
fIsShortSent = true;
|
|
}
|
|
else
|
|
{
|
|
fIsShortSent = false;
|
|
}
|
|
|
|
//-------------------------------------
|
|
// Fill the token pipeline
|
|
//-------------------------------------
|
|
listPos = m_TokList.GetHeadPosition();
|
|
//-- Previous
|
|
pPrev_Tok = NULL;
|
|
//-- Current
|
|
pCur_Tok = m_TokList.GetNext( listPos );
|
|
//-- Next
|
|
if( listPos )
|
|
{
|
|
pNext_Tok = m_TokList.GetNext( listPos );
|
|
}
|
|
else
|
|
{
|
|
pNext_Tok = NULL;
|
|
}
|
|
//-- Next 2
|
|
if( listPos )
|
|
{
|
|
pNext2_Tok = m_TokList.GetNext( listPos );
|
|
}
|
|
else
|
|
{
|
|
pNext2_Tok = NULL;
|
|
}
|
|
//-- Next 3
|
|
if( listPos )
|
|
{
|
|
pNext3_Tok = m_TokList.GetNext( listPos );
|
|
}
|
|
else
|
|
{
|
|
pNext3_Tok = NULL;
|
|
}
|
|
|
|
//-----------------------------------
|
|
// Step through entire word array
|
|
// (skip last)
|
|
//-----------------------------------
|
|
for( iCurWord = 0; iCurWord < (cNumOfWords -1); iCurWord++ )
|
|
{
|
|
bndNum = BND_NoSource;
|
|
accNum = ACC_NoSource;
|
|
|
|
if( (prev_Punct > NULL_BOUNDARY) && (prev_Punct < SUB_BOUNDARY_1) )
|
|
{
|
|
punctDistance = 1;
|
|
fIsYesNo = true;
|
|
fMaybeWH = false;
|
|
fHasDet = false;
|
|
fIsAlphaWH = false;
|
|
}
|
|
else
|
|
{
|
|
punctDistance++;
|
|
}
|
|
//------------------------------------
|
|
// Process new word
|
|
//------------------------------------
|
|
cur_POS = pCur_Tok->POScode;
|
|
cur_Bnd = NULL_BOUNDARY;
|
|
//------------------------------------
|
|
// Don't depend on POS to detect
|
|
// "WH" question
|
|
//------------------------------------
|
|
if( ((pCur_Tok->tokStr[0] == 'W') || (pCur_Tok->tokStr[0] == 'w')) &&
|
|
((pCur_Tok->tokStr[1] == 'H') || (pCur_Tok->tokStr[1] == 'h')) )
|
|
{
|
|
fIsAlphaWH = true;
|
|
}
|
|
else
|
|
{
|
|
fIsAlphaWH = false;
|
|
}
|
|
|
|
//------------------------------------
|
|
// Look ahead to NEXT word
|
|
//------------------------------------
|
|
next_POS = pNext_Tok->POScode;
|
|
if( pNext_Tok->m_TuneBoundaryType != NULL_BOUNDARY )
|
|
{
|
|
fNext_IsPunct = true;
|
|
}
|
|
else
|
|
{
|
|
fNext_IsPunct = false;
|
|
}
|
|
|
|
//------------------------------------
|
|
// Look ahead 2 positions
|
|
//------------------------------------
|
|
if( pNext2_Tok )
|
|
{
|
|
next2_POS = pNext2_Tok->POScode;
|
|
if( pNext2_Tok->m_TuneBoundaryType != NULL_BOUNDARY )
|
|
{
|
|
fNext2_IsPunct = true;
|
|
}
|
|
else
|
|
{
|
|
fNext2_IsPunct = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
next2_POS = MS_Unknown;
|
|
fNext2_IsPunct = false;
|
|
}
|
|
|
|
//------------------------------------
|
|
// Look ahead 3 positions
|
|
//------------------------------------
|
|
if( pNext3_Tok )
|
|
{
|
|
next3_POS = pNext3_Tok->POScode;
|
|
if( pNext3_Tok->m_TuneBoundaryType != NULL_BOUNDARY )
|
|
{
|
|
fNext3_IsPunct = true;
|
|
}
|
|
else
|
|
{
|
|
fNext3_IsPunct = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
next3_POS = MS_Unknown;
|
|
fNext3_IsPunct = false;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// Is phrase a yes/no question?
|
|
//------------------------------------------------------------------------
|
|
if( punctDistance == 1 )
|
|
{
|
|
if( (cur_POS == MS_Interr) || (fIsAlphaWH) )
|
|
{
|
|
//---------------------------------
|
|
// It's a "WH" question
|
|
//---------------------------------
|
|
fIsYesNo = false;
|
|
}
|
|
else if( (cur_POS == MS_Prep) || (cur_POS == MS_Conj) || (cur_POS == MS_CConj) )
|
|
{
|
|
fMaybeWH = true;
|
|
}
|
|
}
|
|
else if( (punctDistance == 2) && (fMaybeWH) &&
|
|
((cur_POS == MS_Interr) || (cur_POS == MS_RelPron) || (fIsAlphaWH)) )
|
|
{
|
|
fIsYesNo = false;
|
|
}
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
// SUB_BOUNDARY_1: Insert boundary after sentence-initial adverb
|
|
//
|
|
// Reluctantly __the cat sat on the mat.
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
if( fInitial_Adv )
|
|
{
|
|
cur_Bnd = SUB_BOUNDARY_1;
|
|
fInitial_Adv = false;
|
|
bndNum = BND_PhraseRule1;
|
|
accNum = ACC_PhraseRule1;
|
|
}
|
|
else
|
|
{
|
|
|
|
if( (punctDistance == 1) &&
|
|
(cur_POS == MS_Adv) && (next_POS == MS_Det) )
|
|
// include
|
|
//LEX_SUBJPRON // he
|
|
//LEX_DPRON // this
|
|
//LEX_IPRON // everybody
|
|
//NOT LEX_PPRON // myself
|
|
{
|
|
fInitial_Adv = true;
|
|
}
|
|
else
|
|
{
|
|
fInitial_Adv = false;
|
|
}
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
// SUB_BOUNDARY_2:Insert boundary before coordinating conjunctions
|
|
// The cat sat on the mat __and cleaned his fur.
|
|
//
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
if( (cur_POS == MS_CConj) &&
|
|
(fHasDet == false) &&
|
|
(punctDistance > 3) &&
|
|
(next2_POS != MS_Conj) )
|
|
{
|
|
cur_Bnd = SUB_BOUNDARY_2;
|
|
bndNum = BND_PhraseRule2;
|
|
accNum = ACC_PhraseRule2;
|
|
}
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
// SUB_BOUNDARY_2:Insert boundary before adverb
|
|
// The cat sat on the mat __reluctantly.
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
else if( (cur_POS == MS_Adv) &&
|
|
(punctDistance > 4) &&
|
|
(next_POS != MS_Adj) )
|
|
{
|
|
cur_Bnd = SUB_BOUNDARY_2;
|
|
bndNum = BND_PhraseRule3;
|
|
accNum = ACC_PhraseRule3;
|
|
}
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
// SUB_BOUNDARY_2:Insert boundary after object pronoun
|
|
// The cat sat with me__ on the mat.
|
|
//
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
else if( (prev_POS == MS_ObjPron) && (punctDistance > 2))
|
|
{
|
|
cur_Bnd = SUB_BOUNDARY_2;
|
|
bndNum = BND_PhraseRule4;
|
|
accNum = ACC_PhraseRule4;
|
|
}
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
// SUB_BOUNDARY_2:Insert boundary before subject pronoun or contraction
|
|
// The cat sat on the mat _I see.
|
|
// The cat sat on the mat _I'm sure.
|
|
//
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
else if( ((cur_POS == MS_SubjPron) || (cur_POS == MS_Contr) ) &&
|
|
(punctDistance > 3) && (prev_POS != MS_RelPron) && (prev_POS != MS_Conj))
|
|
{
|
|
cur_Bnd = SUB_BOUNDARY_2;
|
|
bndNum = BND_PhraseRule5;
|
|
accNum = ACC_PhraseRule5;
|
|
}
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
// SUB_BOUNDARY_2:Insert boundary before interr
|
|
// The cat sat on the mat _how odd.
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
else if( (cur_POS == MS_Interr) && (punctDistance > 4) )
|
|
{
|
|
cur_Bnd = SUB_BOUNDARY_2;
|
|
bndNum = BND_PhraseRule6;
|
|
accNum = ACC_PhraseRule6;
|
|
}
|
|
|
|
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
// SUB_BOUNDARY_3:Insert boundary after subject noun phrase followed by aux verb
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
// SUB_BOUNDARY_3:Insert boundary before vaux after noun phrase
|
|
// The gray cat __should sit on the mat.
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
else if( (punctDistance > 2) &&
|
|
( ((prev_POS == MS_Noun) || (prev_POS == MS_Verb)) && (prev_POS != MS_VAux) ) &&
|
|
(cur_POS == MS_VAux)
|
|
)
|
|
{
|
|
cur_Bnd = SUB_BOUNDARY_3;
|
|
bndNum = BND_PhraseRule7;
|
|
accNum = ACC_PhraseRule7;
|
|
}
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
// SUB_BOUNDARY_3:Insert boundary after MS_Interr
|
|
// The gray cat __should sit on the mat.
|
|
// SEE ABOVE???
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
/*else if( (prev_POS == MS_Noun) && ((next_POS != MS_RelPron) &&
|
|
(next_POS != MS_VAux) && (next_POS != MS_RVAux) &&
|
|
(next2_POS != MS_VAux) && (next2_POS != MS_RVAux)) &&
|
|
(punctDistance > 4) &&
|
|
((cur_POS == MS_VAux) || (cur_POS == MS_RVAux)))
|
|
{
|
|
cur_Bnd = SUB_BOUNDARY_3;
|
|
bndNum = BND_PhraseRule8;
|
|
accNum = ACC_PhraseRule8;
|
|
}*/
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
// SUB_BOUNDARY_3:Insert boundary after MS_Interr
|
|
// The cat sat on the mat _how odd.
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
else if( (prev_POS == MS_Noun) && (next_POS != MS_RelPron) &&
|
|
(next_POS != MS_Conj) &&
|
|
(next_POS != MS_CConj) && (punctDistance > 3) && (cur_POS == MS_Verb))
|
|
{
|
|
cur_Bnd = SUB_BOUNDARY_3;
|
|
bndNum = BND_PhraseRule9;
|
|
accNum = ACC_PhraseRule9;
|
|
}
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
// SUB_BOUNDARY_3:Insert boundary after MS_Interr
|
|
// The cat sat on the mat _how odd.
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
/*else if( (prev_POS == MS_Noun) && (cur_POS != MS_RelPron) &&
|
|
(cur_POS != MS_RVAux) && (cur_POS != MS_CConj) &&
|
|
(cur_POS != MS_Conj) && (punctDistance > 2) &&
|
|
((punctDistance > 2) || (fIsShortSent)) && (cur_POS == MS_Verb))
|
|
{
|
|
cur_Bnd = SUB_BOUNDARY_3;
|
|
bndNum = BND_PhraseRule10;
|
|
accNum = ACC_PhraseRule10;
|
|
}
|
|
|
|
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
// SUB_BOUNDARY_4:Insert boundary before conjunction
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
else if( ((cur_POS == MS_Conj) && (punctDistance > 3) &&
|
|
(fNext_IsPunct == false) &&
|
|
(prev_POS != MS_Conj) && (prev_POS != MS_CConj) &&
|
|
(fNext2_IsPunct == false)) ||
|
|
|
|
( (prev_POS == MS_VPart) && (cur_POS != MS_Prep) &&
|
|
(cur_POS != MS_Det) &&
|
|
(punctDistance > 2) &&
|
|
((cur_POS == MS_Noun) || (cur_POS == MS_Noun) || (cur_POS == MS_Adj))) ||
|
|
|
|
( (cur_POS == MS_Interr) && (punctDistance > 2) &&
|
|
(cur_POS == MS_SubjPron)) )
|
|
{
|
|
cur_Bnd = SUB_BOUNDARY_4;
|
|
bndNum = BND_PhraseRule11;
|
|
accNum = ACC_PhraseRule11;
|
|
}
|
|
|
|
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
// SUB_BOUNDARY_5:Insert boundary before relative pronoun
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
else if( ( (cur_POS == MS_RelPron) && (punctDistance >= 3) &&
|
|
(prev_POS != MS_Prep) && (next3_POS != MS_VAux) &&
|
|
(next3_POS != MS_RVAux) &&
|
|
( (prev_POS == MS_Noun) || (prev_POS == MS_Verb) ) ) ||
|
|
|
|
( (cur_POS == MS_Quant) && (punctDistance > 5) &&
|
|
(prev_POS != MS_Adj) && (prev_POS != MS_Det) &&
|
|
(prev_POS != MS_VAux) && (prev_POS != MS_RVAux) &&
|
|
(prev_POS != MS_Det) && (next2_POS != MS_CConj) &&
|
|
(fNext_IsPunct == false)))
|
|
{
|
|
cur_Bnd = SUB_BOUNDARY_5;
|
|
bndNum = BND_PhraseRule12;
|
|
accNum = ACC_PhraseRule12;
|
|
}*/
|
|
|
|
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
// SUB_BOUNDARY_6:Silverman87-style, content/function tone group boundaries.
|
|
// Does trivial sentence-final function word look-ahead check.
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
|
else if( ( (prev_POS == MS_Noun) || (prev_POS == MS_Verb) || (prev_POS == MS_Adj) || (prev_POS == MS_Adv))
|
|
&& ((cur_POS != MS_Noun) && (cur_POS != MS_Verb) && (cur_POS != MS_Adj) && (cur_POS != MS_Adv))
|
|
&& (fNext_IsPunct == false))
|
|
{
|
|
cur_Bnd = SUB_BOUNDARY_6;
|
|
bndNum = BND_PhraseRule13;
|
|
accNum = ACC_PhraseRule13;
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------
|
|
// If phrasing was found, save it
|
|
//------------------------------------------------------------------------
|
|
if( (cur_Bnd != NULL_BOUNDARY) && (iCurWord > 0) &&
|
|
//!(fNext_IsPunct) &&
|
|
!(prev_Punct) &&
|
|
(pCur_Tok->m_TuneBoundaryType == NULL_BOUNDARY) )
|
|
{
|
|
//pCur_Tok->m_TuneBoundaryType = cur_Bnd;
|
|
pTempTok = new CFEToken;
|
|
if( pTempTok )
|
|
{
|
|
pTempTok->m_TuneBoundaryType = cur_Bnd;
|
|
pTempTok->phon_Len = 1;
|
|
pTempTok->phon_Str[0] = _SIL_;
|
|
pTempTok->srcPosition = pCur_Tok->srcPosition;
|
|
pTempTok->srcLen = pCur_Tok->srcLen;
|
|
pTempTok->tokStr[0] = '+'; // punctuation
|
|
pTempTok->tokStr[1] = 0; // delimiter
|
|
pTempTok->tokLen = 1;
|
|
pTempTok->m_TermSil = 0;
|
|
pTempTok->m_DurScale = 0;
|
|
if( pPrev_Tok )
|
|
{
|
|
pPrev_Tok->m_AccentSource = accNum;
|
|
pPrev_Tok->m_BoundarySource = bndNum;
|
|
pPrev_Tok->m_Accent = K_LHSTAR;
|
|
}
|
|
pTempTok->m_SilenceSource = SIL_SubBound;
|
|
if( pPrev_Tok )
|
|
{
|
|
//pTempTok->m_DurScale = pPrev_Tok->m_DurScale;
|
|
pTempTok->m_ProsodyDurScale = pPrev_Tok->m_ProsodyDurScale;
|
|
pTempTok->user_Volume = pPrev_Tok->user_Volume;
|
|
}
|
|
else
|
|
{
|
|
//pTempTok->m_DurScale = 1.0f;
|
|
pTempTok->m_ProsodyDurScale = 1.0f;
|
|
}
|
|
|
|
m_TokList.InsertBefore( m_TokList.FindIndex( iCurWord ), pTempTok );
|
|
pCur_Tok = pTempTok;
|
|
m_cNumOfWords++;
|
|
cNumOfWords++;
|
|
iCurWord++;
|
|
}
|
|
}
|
|
//-------------------------------
|
|
// Process sentence punctuation
|
|
//-------------------------------
|
|
AdjustQuestTune( pCur_Tok, fIsYesNo );
|
|
|
|
//-------------------------------
|
|
// Prepare for next word
|
|
//-------------------------------
|
|
prev_Punct = pCur_Tok->m_TuneBoundaryType;
|
|
prev_POS = cur_POS;
|
|
pPrev_Tok = pCur_Tok;
|
|
|
|
//------------------------------
|
|
// Shift the token pipeline
|
|
//------------------------------
|
|
pCur_Tok = pNext_Tok;
|
|
pNext_Tok = pNext2_Tok;
|
|
pNext2_Tok = pNext3_Tok;
|
|
if( listPos )
|
|
{
|
|
pNext3_Tok = m_TokList.GetNext( listPos );
|
|
}
|
|
else
|
|
{
|
|
pNext3_Tok = NULL;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// Keep track of when determiners encountered to help in deciding
|
|
// when to allow a strong 'and' boundary (SUB_BOUNDARY_2)
|
|
//------------------------------------------------------------------------
|
|
if( punctDistance > 2)
|
|
{
|
|
fHasDet = false;
|
|
}
|
|
if( cur_POS == MS_Det )
|
|
{
|
|
fHasDet = true;
|
|
}
|
|
}
|
|
//-------------------------------------
|
|
// Process final sentence punctuation
|
|
//-------------------------------------
|
|
pCur_Tok = (CFEToken*)m_TokList.GetTail();
|
|
AdjustQuestTune( pCur_Tok, fIsYesNo );
|
|
}
|
|
|
|
|
|
} /* CFrontend::DoPhrasing */
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::RecalcProsody *
|
|
*--------------------------*
|
|
* Description:
|
|
* In response to a real-time rate change, recalculate duration and pitch
|
|
*
|
|
********************************************************************** MC ***/
|
|
void CFrontend::RecalcProsody()
|
|
{
|
|
SPDBG_FUNC( "CFrontend::RecalcProsody" );
|
|
UNITINFO* pu;
|
|
CAlloCell* pCurCell;
|
|
ULONG k;
|
|
|
|
//--------------------------------------------
|
|
// Compute new allo durations
|
|
//--------------------------------------------
|
|
/*pCurCell = m_pAllos->GetHeadCell();
|
|
while( pCurCell )
|
|
{
|
|
//pCurCell->m_DurScale = 1.0;
|
|
pCurCell = m_pAllos->GetNextCell();
|
|
}*/
|
|
m_DurObj.AlloDuration( m_pAllos, m_RateRatio_API );
|
|
|
|
//--------------------------------------------
|
|
// Modulate allo pitch
|
|
//--------------------------------------------
|
|
m_PitchObj.AlloPitch( m_pAllos, m_BasePitch, m_PitchRange );
|
|
|
|
pu = m_pUnits;
|
|
pCurCell = m_pAllos->GetHeadCell();
|
|
while( pCurCell )
|
|
{
|
|
pu->duration = pCurCell->m_ftDuration;
|
|
for( k = 0; k < pu->nKnots; k++ )
|
|
|
|
{
|
|
pu->pTime[k] = pCurCell->m_ftTime[k] * m_SampleRate;
|
|
pu->pF0[k] = pCurCell->m_ftPitch[k];
|
|
pu->pAmp[k] = pu->ampRatio;
|
|
}
|
|
pu++;
|
|
pCurCell = m_pAllos->GetNextCell();
|
|
}
|
|
} /* CFrontend::RecalcProsody */
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFrontend::NextData *
|
|
*---------------------*
|
|
* Description:
|
|
* This gets called from the backend when UNIT stream is dry.
|
|
* Parse TOKENS to ALLOS to UNITS
|
|
*
|
|
********************************************************************** MC ***/
|
|
HRESULT CFrontend::NextData( void **pData, SPEECH_STATE *pSpeechState )
|
|
{
|
|
SPDBG_FUNC( "CFrontend::NextData" );
|
|
bool haveNewRate = false;
|
|
HRESULT hr = S_OK;
|
|
|
|
//-----------------------------------
|
|
// First, check and see if SAPI has an action
|
|
//-----------------------------------
|
|
// Check for rate change
|
|
long baseRateRatio;
|
|
if( m_pOutputSite->GetActions() & SPVES_RATE )
|
|
{
|
|
hr = m_pOutputSite->GetRate( &baseRateRatio );
|
|
if ( SUCCEEDED( hr ) )
|
|
{
|
|
if( baseRateRatio > SPMAX_VOLUME )
|
|
{
|
|
//--- Clip rate to engine maximum
|
|
baseRateRatio = MAX_USER_RATE;
|
|
}
|
|
else if ( baseRateRatio < MIN_USER_RATE )
|
|
{
|
|
//--- Clip rate to engine minimum
|
|
baseRateRatio = MIN_USER_RATE;
|
|
}
|
|
m_RateRatio_API = CntrlToRatio( baseRateRatio );
|
|
haveNewRate = true;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------
|
|
// Async stop?
|
|
//---------------------------------------------
|
|
if( SUCCEEDED( hr ) && ( m_pOutputSite->GetActions() & SPVES_ABORT ) )
|
|
{
|
|
m_SpeechState = SPEECH_DONE;
|
|
}
|
|
|
|
//---------------------------------------------
|
|
// Async skip?
|
|
//---------------------------------------------
|
|
if( SUCCEEDED( hr ) && ( m_pOutputSite->GetActions() & SPVES_SKIP ) )
|
|
{
|
|
SPVSKIPTYPE SkipType;
|
|
long SkipCount = 0;
|
|
|
|
hr = m_pOutputSite->GetSkipInfo( &SkipType, &SkipCount );
|
|
|
|
if ( SUCCEEDED( hr ) && SkipType == SPVST_SENTENCE )
|
|
{
|
|
IEnumSENTITEM *pGarbage;
|
|
//--- Skip Forwards
|
|
if ( SkipCount > 0 )
|
|
{
|
|
long OriginalSkipCount = SkipCount;
|
|
while ( SkipCount > 1 &&
|
|
( hr = m_pEnumSent->Next( &pGarbage ) ) == S_OK )
|
|
{
|
|
SkipCount--;
|
|
pGarbage->Release();
|
|
}
|
|
if ( hr == S_OK )
|
|
{
|
|
hr = ParseSentence( eNEXT );
|
|
if ( SUCCEEDED( hr ) )
|
|
{
|
|
SkipCount--;
|
|
}
|
|
}
|
|
else if ( hr == S_FALSE )
|
|
{
|
|
m_SpeechState = SPEECH_DONE;
|
|
}
|
|
SkipCount = OriginalSkipCount - SkipCount;
|
|
}
|
|
//--- Skip Backwards
|
|
else if ( SkipCount < 0 )
|
|
{
|
|
long OriginalSkipCount = SkipCount;
|
|
while ( SkipCount < -1 &&
|
|
( hr = m_pEnumSent->Previous( &pGarbage ) ) == S_OK )
|
|
{
|
|
SkipCount++;
|
|
pGarbage->Release();
|
|
}
|
|
if ( hr == S_OK )
|
|
{
|
|
hr = ParseSentence( ePREVIOUS );
|
|
// This case is different from the forward skip, needs to test that
|
|
// Parse sentence found something to parse!
|
|
if ( SUCCEEDED( hr ) && m_SpeechState != SPEECH_DONE)
|
|
{
|
|
SkipCount++;
|
|
}
|
|
}
|
|
else if ( hr == S_FALSE )
|
|
{
|
|
m_SpeechState = SPEECH_DONE;
|
|
}
|
|
SkipCount = OriginalSkipCount - SkipCount;
|
|
}
|
|
//--- Skip to beginning of this sentence
|
|
else
|
|
{
|
|
m_CurUnitIndex = 0;
|
|
}
|
|
hr = m_pOutputSite->CompleteSkip( SkipCount );
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------
|
|
// Make sure we're still speaking
|
|
//---------------------------------------------
|
|
if( SUCCEEDED( hr ) && m_SpeechState != SPEECH_DONE )
|
|
{
|
|
if( m_CurUnitIndex >= m_unitCount)
|
|
{
|
|
//-----------------------------------
|
|
// Get next sentence from Normalizer
|
|
//-----------------------------------
|
|
hr = ParseSentence( eNEXT );
|
|
//m_SpeechState = SPEECH_DONE;
|
|
}
|
|
else if( haveNewRate )
|
|
{
|
|
//-----------------------------------
|
|
// Recalculate prosody to new rate
|
|
//-----------------------------------
|
|
RecalcProsody();
|
|
}
|
|
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
if( m_SpeechState != SPEECH_DONE )
|
|
{
|
|
//-----------------------------------
|
|
// Get next phon
|
|
//-----------------------------------
|
|
m_pUnits[m_CurUnitIndex].hasSpeech = m_HasSpeech;
|
|
*pData =( void*)&m_pUnits[m_CurUnitIndex];
|
|
m_CurUnitIndex++;
|
|
}
|
|
}
|
|
}
|
|
//-------------------------------------------
|
|
// Let client know if text input is dry
|
|
//-------------------------------------------
|
|
*pSpeechState = m_SpeechState;
|
|
|
|
return hr;
|
|
} /* CFrontend::NextData */
|
|
|