/******************************************************************************* * StdSentEnum.cpp * *-----------------* * Description: * This module is the main implementation file for the CStdSentEnum class. *------------------------------------------------------------------------------- * Created By: EDC Date: 03/19/99 * Copyright (C) 1999 Microsoft Corporation * All Rights Reserved * *******************************************************************************/ //--- Additional includes #include "stdafx.h" #ifndef StdSentEnum_h #include "stdsentenum.h" #endif #include "spttsengdebug.h" #include "SpAutoObjectLock.h" //--- Locals CComAutoCriticalSection CStdSentEnum::m_AbbrevTableCritSec; //=== CStdSentEnum ============================================================ // /***************************************************************************** * CStdSentEnum::InitPron * *------------------------* * Description: * Inits pron tables ********************************************************************* AH ***/ HRESULT CStdSentEnum::InitPron( WCHAR** OriginalPron ) { HRESULT hr = S_OK; WCHAR *NewPron = NULL; NewPron = new WCHAR[ wcslen( *OriginalPron ) ]; hr = m_cpPhonemeConverter->PhoneToId( *OriginalPron, NewPron ); if ( SUCCEEDED( hr ) ) { *OriginalPron = NewPron; } return hr; } /* InitPron */ /***************************************************************************** * CStdSentEnum::FinalConstruct * *------------------------------* * Description: * Constructor ********************************************************************* EDC ***/ HRESULT CStdSentEnum::FinalConstruct() { SPDBG_FUNC( "CStdSentEnum::FinalConstruct" ); HRESULT hr = S_OK; m_dwSpeakFlags = 0; m_pTextFragList = NULL; m_pMorphLexicon = NULL; m_fHaveNamesLTS = false; m_eSeparatorAndDecimal = COMMA_PERIOD; m_eShortDateOrder = MONTH_DAY_YEAR; /*** Create phone converter ***/ if ( SUCCEEDED( hr ) ) { hr = SpCreatePhoneConverter( 1033, NULL, NULL, &m_cpPhonemeConverter ); m_AbbrevTableCritSec.Lock(); if ( !g_fAbbrevTablesInitialized ) { for ( ULONG i = 0; SUCCEEDED( hr ) && i < sp_countof( g_AbbreviationTable ); i++ ) { if ( g_AbbreviationTable[i].pPron1 ) { hr = InitPron( &g_AbbreviationTable[i].pPron1 ); } if ( SUCCEEDED( hr ) && g_AbbreviationTable[i].pPron2 ) { hr = InitPron( &g_AbbreviationTable[i].pPron2 ); } if ( SUCCEEDED( hr ) && g_AbbreviationTable[i].pPron3 ) { hr = InitPron( &g_AbbreviationTable[i].pPron3 ); } } for ( i = 0; SUCCEEDED( hr ) && i < sp_countof( g_AmbiguousWordTable ); i++ ) { if ( g_AmbiguousWordTable[i].pPron1 ) { hr = InitPron( &g_AmbiguousWordTable[i].pPron1 ); } if ( SUCCEEDED( hr ) && g_AmbiguousWordTable[i].pPron2 ) { hr = InitPron( &g_AmbiguousWordTable[i].pPron2 ); } if ( SUCCEEDED( hr ) && g_AmbiguousWordTable[i].pPron3 ) { hr = InitPron( &g_AmbiguousWordTable[i].pPron3 ); } } for ( i = 0; SUCCEEDED( hr ) && i < sp_countof( g_PostLexLookupWordTable ); i++ ) { if ( g_PostLexLookupWordTable[i].pPron1 ) { hr = InitPron( &g_PostLexLookupWordTable[i].pPron1 ); } if ( SUCCEEDED( hr ) && g_PostLexLookupWordTable[i].pPron2 ) { hr = InitPron( &g_PostLexLookupWordTable[i].pPron2 ); } if ( SUCCEEDED( hr ) && g_PostLexLookupWordTable[i].pPron3 ) { hr = InitPron( &g_PostLexLookupWordTable[i].pPron3 ); } } if ( SUCCEEDED( hr ) ) { hr = InitPron( &g_pOfA ); if ( SUCCEEDED( hr ) ) { hr = InitPron( &g_pOfAn ); } } } if ( SUCCEEDED( hr ) ) { g_fAbbrevTablesInitialized = true; } m_AbbrevTableCritSec.Unlock(); } return hr; } /* CStdSentEnum::FinalConstruct */ /***************************************************************************** * CStdSentEnum::FinalRelease * *----------------------------* * Description: * Destructor ********************************************************************* EDC ***/ void CStdSentEnum::FinalRelease() { SPDBG_FUNC( "CStdSentEnum::FinalRelease" ); if ( m_pMorphLexicon ) { delete m_pMorphLexicon; } } /* CStdSentEnum::FinalRelease */ /***************************************************************************** * CStdSentEnum::SetFragList * *---------------------------* * The text fragment list passed in is guaranteed to be valid for the lifetime * of this object. Each time this method is called, the sentence enumerator * should reset its state. ********************************************************************* EDC ***/ STDMETHODIMP CStdSentEnum:: SetFragList( const SPVTEXTFRAG* pTextFragList, DWORD dwSpeakFlags ) { SPAUTO_OBJ_LOCK; SPDBG_FUNC( "CStdSentEnum::SetFragList" ); HRESULT hr = S_OK; //--- Check args if( SP_IS_BAD_READ_PTR( pTextFragList ) || ( dwSpeakFlags & SPF_UNUSED_FLAGS ) ) { hr = E_INVALIDARG; } else { m_dwSpeakFlags = dwSpeakFlags; m_pTextFragList = pTextFragList; //--- grab normalization preferences from the registry if ( SUCCEEDED( hr ) ) { CComPtr cpToken; CSpDynamicString dstrTokenKeyName; hr = StringFromCLSID( CLSID_MSE_TTSEngine, &dstrTokenKeyName ); if ( SUCCEEDED( hr ) ) { hr = SpCreateNewToken( L"HKEY_CURRENT_USER\\Software\\Microsoft\\Speech\\Voices", dstrTokenKeyName, &cpToken ); } if ( SUCCEEDED( hr ) ) { DWORD dwTemp; if ( SUCCEEDED( cpToken->GetDWORD( L"SeparatorAndDecimal", &dwTemp ) ) ) { m_eSeparatorAndDecimal = (SEPARATOR_AND_DECIMAL) dwTemp; } if ( SUCCEEDED( cpToken->GetDWORD( L"ShortDateOrder", &dwTemp ) ) ) { m_eShortDateOrder = (SHORT_DATE_ORDER) dwTemp; } } } //--- Reset state Reset(); } return hr; } /* CStdSentEnum::SetFragList */ /***************************************************************************** * CStdSentEnum::Next * *--------------------* * ********************************************************************* EDC ***/ STDMETHODIMP CStdSentEnum::Next( IEnumSENTITEM **ppSentItemEnum ) { SPAUTO_OBJ_LOCK; SPDBG_FUNC( "CStdSentEnum::Next" ); HRESULT hr = S_OK; //--- Check args if( SPIsBadWritePtr( ppSentItemEnum, sizeof( IEnumSENTITEM* ) ) ) { hr = E_INVALIDARG; } else { //--- If this is NULL then the enum needs to be reset if( m_pCurrFrag ) { SentencePointer NewSentencePointer; NewSentencePointer.pSentenceFrag = m_pCurrFrag; NewSentencePointer.pSentenceStart = m_pNextChar; hr = GetNextSentence( ppSentItemEnum ); if( hr == S_OK ) { //--- Update Sentence Pointer List hr = m_SentenceStack.Push( NewSentencePointer ); } } else { hr = S_FALSE; } } return hr; } /* CStdSentEnum::Next */ /***************************************************************************** * CStdSentEnum::Previous * *--------------------* * ********************************************************************* AH ****/ STDMETHODIMP CStdSentEnum::Previous( IEnumSENTITEM **ppSentItemEnum ) { SPAUTO_OBJ_LOCK; SPDBG_FUNC( "CStdSentEnum::Previous" ); HRESULT hr = S_OK; //--- Check args if( SPIsBadWritePtr( ppSentItemEnum, sizeof( IEnumSENTITEM* ) ) ) { hr = E_INVALIDARG; } else { //--- Don't care if m_pCurrFrag is NULL, as long as we have enough on the SentenceStack //--- to skip backwards... if( m_SentenceStack.GetCount() >= 2 ) { //--- Get the previous Sentence from the Sentence List, and then remove the Current Sentence SentencePointer &PreviousSentence = m_SentenceStack.Pop(); PreviousSentence = m_SentenceStack.Pop(); //--- Reset the current frag and the current text pointer position m_pCurrFrag = PreviousSentence.pSentenceFrag; m_pNextChar = PreviousSentence.pSentenceStart; m_pEndChar = m_pCurrFrag->pTextStart + m_pCurrFrag->ulTextLen; hr = GetNextSentence( ppSentItemEnum ); if( hr == S_OK ) { //--- Update Sentence Pointer List hr = m_SentenceStack.Push( PreviousSentence ); } } else { hr = S_FALSE; } } return hr; } /* CStdSentEnum::Previous */ /***************************************************************************** * SkipWhiteSpaceAndTags * *-----------------------* * Skips m_pNextChar ahead to the next non-whitespace character (skipping * ahead in the frag list, if necessary) or sets it to NULL if it hits the * end of the frag list text... ********************************************************************* AH ****/ HRESULT CStdSentEnum::SkipWhiteSpaceAndTags( const WCHAR*& pStartChar, const WCHAR*& pEndChar, const SPVTEXTFRAG*& pCurrFrag, CSentItemMemory& MemoryManager, BOOL fAddToItemList, CItemList* pItemList ) { SPDBG_ASSERT( pStartChar <= pEndChar ); HRESULT hr = S_OK; while ( pStartChar && ( IsSpace( *pStartChar ) || pStartChar == pEndChar ) ) { //--- Skip whitespace while ( pStartChar < pEndChar && IsSpace( *pStartChar ) ) { ++pStartChar; } //--- Skip to next spoken frag, if necessary if ( pStartChar == pEndChar ) { pCurrFrag = pCurrFrag->pNext; while ( pCurrFrag && pCurrFrag->State.eAction != SPVA_Speak && pCurrFrag->State.eAction != SPVA_SpellOut ) { pStartChar = (WCHAR*) pCurrFrag->pTextStart; pEndChar = (WCHAR*) pStartChar + pCurrFrag->ulTextLen; //--- Add non-spoken fragments, if fAddToItemList is true. if ( fAddToItemList ) { //-- Check for names lexicon XML tag... if( !m_fNameItem && m_pCurrFrag->ulTextLen == 6 && !_wcsnicmp( L"", m_pCurrFrag->pTextStart, m_pCurrFrag->ulTextLen ) ) { m_fNameItem = true; } else if( m_fNameItem && m_pCurrFrag->ulTextLen == 7 && !_wcsnicmp( L"", m_pCurrFrag->pTextStart, m_pCurrFrag->ulTextLen ) ) { m_fNameItem = false; } CSentItem Item; Item.pItemSrcText = pCurrFrag->pTextStart; Item.ulItemSrcLen = pCurrFrag->ulTextLen; Item.ulItemSrcOffset = pCurrFrag->ulTextSrcOffset; Item.ulNumWords = 1; Item.Words = (TTSWord*) MemoryManager.GetMemory( sizeof(TTSWord), &hr ); if ( SUCCEEDED( hr ) ) { ZeroMemory( Item.Words, sizeof(TTSWord) ); Item.Words[0].pXmlState = &pCurrFrag->State; Item.Words[0].eWordPartOfSpeech = MS_Unknown; Item.eItemPartOfSpeech = MS_Unknown; Item.pItemInfo = (TTSItemInfo*) MemoryManager.GetMemory( sizeof(TTSItemInfo), &hr ); if ( SUCCEEDED( hr ) ) { Item.pItemInfo->Type = eWORDLIST_IS_VALID; pItemList->AddTail( Item ); } } } pCurrFrag = pCurrFrag->pNext; } if ( !pCurrFrag ) { pStartChar = NULL; pEndChar = NULL; } else { pStartChar = (WCHAR*) pCurrFrag->pTextStart; pEndChar = (WCHAR*) pStartChar + pCurrFrag->ulTextLen; } } } return hr; } /* SkipWhiteSpaceAndTags */ /***************************************************************************** * FindTokenEnd * *--------------* * Returns the position of the first whitespace character after pStartChar, * or pEndChar, or the character after SP_MAX_WORD_LENGTH, whichever comes first. ********************************************************************* AH ****/ const WCHAR* CStdSentEnum::FindTokenEnd( const WCHAR* pStartChar, const WCHAR* pEndChar ) { SPDBG_ASSERT( pStartChar < pEndChar ); ULONG ulNumChars = 1; const WCHAR *pPos = pStartChar; while ( pPos && pPos < pEndChar && !IsSpace( *pPos ) && ulNumChars < SP_MAX_WORD_LENGTH ) { pPos++; ulNumChars++; } return pPos; } /* FindTokenEnd */ /***************************************************************************** * CStdSentEnum::AddNextSentItem * *-------------------------------* * Locates the next sentence item in the stream and adds it to the list. * Returns true if the last item added is the end of the sentence. ********************************************************************* AH ****/ HRESULT CStdSentEnum::AddNextSentItem( CItemList& ItemList, CSentItemMemory& MemoryManager, BOOL* pfIsEOS ) { SPDBG_ASSERT( m_pNextChar && pfIsEOS ); HRESULT hr = S_OK; BOOL fHitPauseItem = false; CSentItem Item; ULONG ulTrailItems = 0; TTSItemType ItemType = eUNMATCHED; *pfIsEOS = false; //--- Skip initial whitespace characters and XML markup (by skipping ahead in the frag list). hr = SkipWhiteSpaceAndTags( m_pNextChar, m_pEndChar, m_pCurrFrag, MemoryManager, true, &ItemList ); //--- This will happen when we hit the end of the frag list if ( !m_pNextChar ) { return S_OK; } //--- Find end of the next token (next whitespace character, hyphen, or m_pEndChar). m_pEndOfCurrToken = FindTokenEnd( m_pNextChar, m_pEndChar ); //--- Get Primary Insert Position SPLISTPOS ItemPos = ItemList.AddTail( Item ); //--- Try looking up this token in the User Lexicon... WCHAR Temp = *( (WCHAR*) m_pEndOfCurrToken ); *( (WCHAR*) m_pEndOfCurrToken ) = 0; SPWORDPRONUNCIATIONLIST SPList; ZeroMemory( &SPList, sizeof( SPWORDPRONUNCIATIONLIST ) ); hr = m_cpAggregateLexicon->GetPronunciations( m_pNextChar, 1033, eLEXTYPE_USER, &SPList ); if( SPList.pvBuffer ) { ::CoTaskMemFree( SPList.pvBuffer ); } *( (WCHAR*) m_pEndOfCurrToken ) = Temp; if ( SUCCEEDED( hr ) ) { Item.eItemPartOfSpeech = MS_Unknown; Item.pItemSrcText = m_pNextChar; Item.ulItemSrcLen = (ULONG) ( m_pEndOfCurrToken - m_pNextChar ); Item.ulItemSrcOffset = m_pCurrFrag->ulTextSrcOffset + (ULONG)( m_pNextChar - m_pCurrFrag->pTextStart ); Item.ulNumWords = 1; Item.Words = (TTSWord*) MemoryManager.GetMemory( sizeof(TTSWord), &hr ); if ( SUCCEEDED( hr ) ) { ZeroMemory( Item.Words, sizeof(TTSWord) ); Item.Words[0].pXmlState = &m_pCurrFrag->State; Item.Words[0].pWordText = m_pNextChar; Item.Words[0].ulWordLen = Item.ulItemSrcLen; Item.Words[0].pLemma = Item.Words[0].pWordText; Item.Words[0].ulLemmaLen = Item.Words[0].ulWordLen; Item.Words[0].eWordPartOfSpeech = MS_Unknown; Item.eItemPartOfSpeech = MS_Unknown; Item.pItemInfo = (TTSItemInfo*) MemoryManager.GetMemory( sizeof(TTSItemInfo*), &hr ); if ( SUCCEEDED( hr ) ) { Item.pItemInfo->Type = eALPHA_WORD; ItemList.SetAt( ItemPos, Item ); } } m_pNextChar = m_pEndOfCurrToken; } //--- Not in the user lex - itemize, normalize, etc. else if ( hr == SPERR_NOT_IN_LEX ) { hr = S_OK; //--- convert text from Unicode to Ascii hr = DoUnicodeToAsciiMap( m_pNextChar, (ULONG)( m_pEndOfCurrToken - m_pNextChar ), (WCHAR*)m_pNextChar ); if ( SUCCEEDED( hr ) ) { //--- Find end of the next token (next whitespace character, hyphen, or m_pEndChar) //--- AGAIN, since the mapping may have introduced new whitespace characters... m_pEndOfCurrToken = FindTokenEnd( m_pNextChar, m_pEndChar ); //--- Insert lead items (group beginnings, quotation marks) while ( m_pNextChar < m_pEndOfCurrToken && ( ( ItemType = IsGroupBeginning( *m_pNextChar ) ) != eUNMATCHED || ( ItemType = IsQuotationMark( *m_pNextChar ) ) != eUNMATCHED ) ) { CSentItem LeadItem; LeadItem.pItemSrcText = m_pNextChar; LeadItem.ulItemSrcLen = 1; LeadItem.ulItemSrcOffset = m_pCurrFrag->ulTextSrcOffset + (ULONG)(( m_pNextChar - m_pCurrFrag->pTextStart )); LeadItem.ulNumWords = 1; LeadItem.Words = (TTSWord*) MemoryManager.GetMemory( sizeof(TTSWord), &hr ); if ( SUCCEEDED( hr ) ) { ZeroMemory( LeadItem.Words, sizeof(TTSWord) ); LeadItem.Words[0].pXmlState = &m_pCurrFrag->State; LeadItem.Words[0].eWordPartOfSpeech = ConvertItemTypeToPartOfSp( ItemType ); LeadItem.eItemPartOfSpeech = ConvertItemTypeToPartOfSp( ItemType ); LeadItem.pItemInfo = (TTSItemInfo*) MemoryManager.GetMemory( sizeof(TTSItemInfo), &hr ); if ( SUCCEEDED( hr ) ) { LeadItem.pItemInfo->Type = ItemType; if ( m_dwSpeakFlags & SPF_NLP_SPEAK_PUNC || m_pCurrFrag->State.eAction == SPVA_SpellOut ) { CWordList TempWordList; ExpandPunctuation( TempWordList, *m_pNextChar ); hr = SetWordList( LeadItem, TempWordList, MemoryManager ); LeadItem.pItemInfo->Type = eUNMATCHED; } ItemList.InsertBefore( ItemPos, LeadItem ); m_pNextChar++; } } ItemType = eUNMATCHED; } //--- Insert trail items (group endings, quotation marks, misc. punctuation, EOS Items) m_pEndOfCurrItem = m_pEndOfCurrToken; BOOL fAddTrailItem = true; BOOL fAbbreviation = false; while ( (m_pEndOfCurrItem - 1) >= m_pNextChar && fAddTrailItem ) { fAddTrailItem = false; fAbbreviation = false; //--- Check group endings, quotation marks, misc. punctuation. if ( ( ItemType = IsGroupEnding( *(m_pEndOfCurrItem - 1) ) ) != eUNMATCHED || ( ItemType = IsQuotationMark( *(m_pEndOfCurrItem - 1) ) ) != eUNMATCHED || ( ItemType = IsMiscPunctuation( *(m_pEndOfCurrItem - 1) ) ) != eUNMATCHED ) { fAddTrailItem = true; if ( ItemType == eCOMMA || ItemType == eCOLON || ItemType == eSEMICOLON ) { fHitPauseItem = true; } } //--- Check EOS Items, except periods preceded by alpha characters else if ( ( ItemType = IsEOSItem( *(m_pEndOfCurrItem - 1) ) ) != eUNMATCHED && ! ( ItemType == ePERIOD && ( m_pEndOfCurrItem - 2 >= m_pNextChar ) && ( iswalpha( *(m_pEndOfCurrItem - 2) ) ) ) ) { //--- Check for ellipses if ( ItemType == ePERIOD ) { if ( m_pEndOfCurrItem == m_pEndOfCurrToken && ( m_pEndOfCurrItem - 2 >= m_pNextChar ) && ( ( ItemType = IsEOSItem( *(m_pEndOfCurrItem - 2) ) ) == ePERIOD ) && ( m_pEndOfCurrItem - 3 == m_pNextChar ) && ( ( ItemType = IsEOSItem( *(m_pEndOfCurrItem - 3) ) ) == ePERIOD ) ) { fAddTrailItem = true; ItemType = eELLIPSIS; } else { ItemType = ePERIOD; fAddTrailItem = true; *pfIsEOS = true; } } else { fAddTrailItem = true; *pfIsEOS = true; } } //--- Period preceded by alpha character - determine whether it is EOS. else if ( ItemType == ePERIOD ) { //--- Is it an Initialism ( e.g. "e.g." )? If so, only EOS if the next //--- word is in the common first words list... hr = IsInitialism( ItemList, ItemPos, MemoryManager, pfIsEOS ); if ( SUCCEEDED( hr ) ) { if ( *pfIsEOS ) { //--- Did we see a pause item earlier? In that case, we should NOT listen to this //--- IsEOS decision from IsInitialism... if ( fHitPauseItem ) { *pfIsEOS = false; } else { fAddTrailItem = true; fAbbreviation = true; } } } else if ( hr == E_INVALIDARG ) { const WCHAR temp = (WCHAR) *( m_pEndOfCurrItem - 1 ); *( (WCHAR*) ( m_pEndOfCurrItem - 1 ) ) = 0; const AbbrevRecord* pAbbrevRecord = (AbbrevRecord*) bsearch( (void*) m_pNextChar, (void*) g_AbbreviationTable, sp_countof( g_AbbreviationTable ), sizeof( AbbrevRecord ), CompareStringAndAbbrevRecord ); *( (WCHAR*) ( m_pEndOfCurrItem - 1 ) ) = temp; if ( pAbbrevRecord ) { //--- Matched an abbreviation if ( pAbbrevRecord->iSentBreakDisambig < 0 ) { //--- Abbreviation will never end a sentence - just insert into ItemList *pfIsEOS = false; hr = S_OK; Item.pItemSrcText = m_pNextChar; Item.ulItemSrcLen = (ULONG)(m_pEndOfCurrItem - m_pNextChar); Item.ulItemSrcOffset = m_pCurrFrag->ulTextSrcOffset + (ULONG)( m_pNextChar - m_pCurrFrag->pTextStart ); Item.ulNumWords = 1; Item.Words = (TTSWord*) MemoryManager.GetMemory( sizeof( TTSWord ), &hr ); if ( SUCCEEDED( hr ) ) { ZeroMemory( Item.Words, sizeof( TTSWord ) ); Item.Words[0].pXmlState = &m_pCurrFrag->State; Item.Words[0].pWordText = Item.pItemSrcText; Item.Words[0].ulWordLen = Item.ulItemSrcLen; Item.Words[0].pLemma = Item.pItemSrcText; Item.Words[0].ulLemmaLen = Item.ulItemSrcLen; Item.pItemInfo = (TTSItemInfo*) MemoryManager.GetMemory( sizeof(TTSAbbreviationInfo), &hr ); if ( SUCCEEDED( hr ) ) { if ( NeedsToBeNormalized( pAbbrevRecord ) ) { Item.pItemInfo->Type = eABBREVIATION_NORMALIZE; } else { Item.pItemInfo->Type = eABBREVIATION; } ( (TTSAbbreviationInfo*) Item.pItemInfo )->pAbbreviation = pAbbrevRecord; ItemList.SetAt( ItemPos, Item ); } } } else { //--- Need to do some disambiguation to determine whether, //--- a) this is indeed an abbreviation (e.g. "Ed.") //--- b) the period doubles as EOS hr = ( this->*g_SentBreakDisambigTable[pAbbrevRecord->iSentBreakDisambig] ) ( pAbbrevRecord, ItemList, ItemPos, MemoryManager, pfIsEOS ); if ( SUCCEEDED( hr ) ) { if ( *pfIsEOS ) { if ( fHitPauseItem ) { *pfIsEOS = false; } else { fAddTrailItem = true; fAbbreviation = true; } } } } } if ( hr == E_INVALIDARG ) { //--- Just check for periods internal to the item - this catches stuff like //--- 10:30p.m. for ( const WCHAR* pIterator = m_pNextChar; pIterator < m_pEndOfCurrItem - 1; pIterator++ ) { if ( *pIterator == L'.' ) { *pfIsEOS = false; break; } } //--- If all previous checks have failed, it is EOS. if ( pIterator == ( m_pEndOfCurrItem - 1 ) && !fHitPauseItem ) { hr = S_OK; fAddTrailItem = true; *pfIsEOS = true; } else if ( hr == E_INVALIDARG ) { hr = S_OK; } } } } //--- Add trail item. if ( fAddTrailItem ) { ulTrailItems++; CSentItem TrailItem; if ( ItemType == eELLIPSIS ) { TrailItem.pItemSrcText = m_pEndOfCurrItem - 3; TrailItem.ulItemSrcLen = 3; TrailItem.ulItemSrcOffset = m_pCurrFrag->ulTextSrcOffset + (ULONG)( m_pEndOfCurrItem - m_pCurrFrag->pTextStart - 3 ); } else { TrailItem.pItemSrcText = m_pEndOfCurrItem - 1; TrailItem.ulItemSrcLen = 1; TrailItem.ulItemSrcOffset = m_pCurrFrag->ulTextSrcOffset + (ULONG)( m_pEndOfCurrItem - m_pCurrFrag->pTextStart - 1 ); } TrailItem.ulNumWords = 1; TrailItem.Words = (TTSWord*) MemoryManager.GetMemory( sizeof(TTSWord), &hr ); if ( SUCCEEDED( hr ) ) { ZeroMemory( TrailItem.Words, sizeof(TTSWord) ); TrailItem.Words[0].pXmlState = &m_pCurrFrag->State; TrailItem.Words[0].eWordPartOfSpeech = ConvertItemTypeToPartOfSp( ItemType ); TrailItem.eItemPartOfSpeech = ConvertItemTypeToPartOfSp( ItemType ); TrailItem.pItemInfo = (TTSItemInfo*) MemoryManager.GetMemory( sizeof(TTSItemInfo), &hr ); if ( SUCCEEDED( hr ) ) { TrailItem.pItemInfo->Type = ItemType; if ( m_dwSpeakFlags & SPF_NLP_SPEAK_PUNC || ( m_pCurrFrag->State.eAction == SPVA_SpellOut && !fAbbreviation ) ) { CWordList TempWordList; ExpandPunctuation( TempWordList, *(m_pEndOfCurrItem - 1) ); hr = SetWordList( TrailItem, TempWordList, MemoryManager ); TrailItem.pItemInfo->Type = eUNMATCHED; } ItemList.InsertAfter( ItemPos, TrailItem ); if ( !fAbbreviation ) { if ( ItemType == eELLIPSIS ) { m_pEndOfCurrItem -= 3; ulTrailItems = 3; } else { m_pEndOfCurrItem--; } } } } ItemType = eUNMATCHED; if ( fAbbreviation ) { break; } } } //--- Do Main Item Insertion if ( SUCCEEDED( hr ) && m_pNextChar == m_pEndOfCurrItem ) { ItemList.RemoveAt( ItemPos ); } else if ( SUCCEEDED( hr ) ) { hr = Normalize( ItemList, ItemPos, MemoryManager ); } if( m_fNameItem ) { wcscpy( ItemList.GetAt( ItemPos ).CustomLtsToken, L"Names" ); } //--- Advance m_pNextChar to m_pEndOfCurrItem + once for each trail item matched. if ( SUCCEEDED( hr ) ) { if ( !fAbbreviation && m_pEndOfCurrItem + ulTrailItems != m_pEndOfCurrToken ) { //--- Multi-token item matched in Normalize()... Remove all previously matched trail items, //--- as they were matched as part of the larger item... m_pNextChar = m_pEndOfCurrItem; Item = ItemList.GetNext( ItemPos ); while ( ItemPos ) { SPLISTPOS RemovePos = ItemPos; Item = ItemList.GetNext( ItemPos ); ItemList.RemoveAt( RemovePos ); } } else { m_pNextChar = m_pEndOfCurrToken; } } } } return hr; } /* CStdSentEnum::AddNextSentItem */ /***************************************************************************** * CStdSentEnum::GetNextSentence * *-------------------------------* * This method is used to create a sentence item enumerator and populate it * with items. If the SPF_NLP_PASSTHROUGH flag is set, each item is the block * of text between XML states. If the SPF_NLP_PASSTHROUGH flag is not set, each * item is an individual word that is looked up in the current lexicon(s). ********************************************************************* EDC ***/ HRESULT CStdSentEnum::GetNextSentence( IEnumSENTITEM** ppItemEnum ) { HRESULT hr = S_OK; ULONG ulNumItems = 0; const SPVTEXTFRAG* pPrevFrag = m_pCurrFrag; //--- Is there any work to do if( m_pCurrFrag == NULL ) return S_FALSE; //--- Create sentence enum CComObject *pItemEnum; hr = CComObject::CreateInstance( &pItemEnum ); if( SUCCEEDED( hr ) ) { pItemEnum->AddRef(); pItemEnum->_SetOwner( GetControllingUnknown() ); *ppItemEnum = pItemEnum; } if( SUCCEEDED( hr ) ) { BOOL fSentDone = false; BOOL fGoToNextFrag = false; CItemList& ItemList = pItemEnum->_GetList(); CSentItemMemory& MemoryManager = pItemEnum->_GetMemoryManager(); while( SUCCEEDED(hr) && m_pCurrFrag && !fSentDone && ulNumItems < 50 ) { ulNumItems++; if( m_pCurrFrag->State.eAction == SPVA_Speak || m_pCurrFrag->State.eAction == SPVA_SpellOut ) { hr = AddNextSentItem( ItemList, MemoryManager, &fSentDone ); //--- Advance fragment? if( SUCCEEDED( hr ) && m_pNextChar && m_pEndChar && m_pNextChar >= m_pEndChar ) { fGoToNextFrag = true; } } else { //-- Check for lexicon if( !m_fNameItem && m_pCurrFrag->ulTextLen == 6 && !_wcsnicmp( L"", m_pCurrFrag->pTextStart, m_pCurrFrag->ulTextLen ) ) { m_fNameItem = true; } else if( m_fNameItem && m_pCurrFrag->ulTextLen == 7 && !_wcsnicmp( L"", m_pCurrFrag->pTextStart, m_pCurrFrag->ulTextLen ) ) { m_fNameItem = false; } //--- Add non spoken fragments CSentItem Item; Item.pItemSrcText = m_pCurrFrag->pTextStart; Item.ulItemSrcLen = m_pCurrFrag->ulTextLen; Item.ulItemSrcOffset = m_pCurrFrag->ulTextSrcOffset; Item.ulNumWords = 1; Item.Words = (TTSWord*) MemoryManager.GetMemory( sizeof(TTSWord), &hr ); if ( SUCCEEDED( hr ) ) { ZeroMemory( Item.Words, sizeof(TTSWord) ); Item.Words[0].pXmlState = &m_pCurrFrag->State; Item.Words[0].eWordPartOfSpeech = MS_Unknown; Item.eItemPartOfSpeech = MS_Unknown; Item.pItemInfo = (TTSItemInfo*) MemoryManager.GetMemory( sizeof(TTSItemInfo), &hr ); if ( SUCCEEDED( hr ) ) { Item.pItemInfo->Type = eWORDLIST_IS_VALID; ItemList.AddTail( Item ); } } fGoToNextFrag = true; } if( SUCCEEDED( hr ) && fGoToNextFrag ) { fGoToNextFrag = false; pPrevFrag = m_pCurrFrag; m_pCurrFrag = m_pCurrFrag->pNext; if( m_pCurrFrag ) { m_pNextChar = m_pCurrFrag->pTextStart; m_pEndChar = m_pNextChar + m_pCurrFrag->ulTextLen; } else { m_pNextChar = NULL; m_pEndChar = NULL; } } } // end while //--- If no period has been added, add one now - this will happen if the text //--- is ONLY XML markup... if ( SUCCEEDED(hr) && !fSentDone ) { CSentItem EOSItem; EOSItem.pItemSrcText = g_period.pStr; EOSItem.ulItemSrcLen = g_period.Len; EOSItem.ulItemSrcOffset = pPrevFrag->ulTextSrcOffset + pPrevFrag->ulTextLen; EOSItem.ulNumWords = 1; EOSItem.Words = (TTSWord*) MemoryManager.GetMemory( sizeof(TTSWord), &hr ); if ( SUCCEEDED( hr ) ) { ZeroMemory( EOSItem.Words, sizeof(TTSWord) ); EOSItem.Words[0].pXmlState = &g_DefaultXMLState; EOSItem.Words[0].eWordPartOfSpeech = MS_EOSItem; EOSItem.eItemPartOfSpeech = MS_EOSItem; EOSItem.pItemInfo = (TTSItemInfo*) MemoryManager.GetMemory( sizeof(TTSItemInfo), &hr ); if ( SUCCEEDED( hr ) ) { EOSItem.pItemInfo->Type = ePERIOD; ItemList.AddTail( EOSItem ); } } } //--- Output debugging information, if sentence breaks are desired TTSDBG_LOGITEMLIST( pItemEnum->_GetList(), STREAM_SENTENCEBREAKS ); if( SUCCEEDED( hr ) ) { hr = DetermineProns( pItemEnum->_GetList(), pItemEnum->_GetMemoryManager() ); } pItemEnum->Reset(); //--- Output debugging information, if POS or Pronunciations are desired TTSDBG_LOGITEMLIST( pItemEnum->_GetList(), STREAM_LEXLOOKUP ); } return hr; } /* CStdSentEnum::GetNextSentence */ /***************************************************************************** * CStdSentEnum::Reset * *---------------------* * ********************************************************************* EDC ***/ STDMETHODIMP CStdSentEnum::Reset( void ) { SPAUTO_OBJ_LOCK; SPDBG_FUNC( "CStdSentEnum::Reset" ); HRESULT hr = S_OK; m_pCurrFrag = m_pTextFragList; m_pNextChar = m_pCurrFrag->pTextStart; m_pEndChar = m_pNextChar + m_pCurrFrag->ulTextLen; m_SentenceStack.Reset(); m_fNameItem = false; return hr; } /* CStdSentEnum::Reset */ /***************************************************************************** * CStdSentEnum::InitAggregateLexicon * *------------------------------------* * ********************************************************************* AH ****/ HRESULT CStdSentEnum::InitAggregateLexicon( void ) { return m_cpAggregateLexicon.CoCreateInstance(CLSID_SpLexicon); } /***************************************************************************** * CStdSentEnum::AddLexiconToAggregate * *-------------------------------------* * ********************************************************************* AH ****/ HRESULT CStdSentEnum::AddLexiconToAggregate( ISpLexicon *pAddLexicon, DWORD dwFlags ) { return m_cpAggregateLexicon->AddLexicon( pAddLexicon, dwFlags ); } /***************************************************************************** * CStdSentEnum::InitMorphLexicon * *--------------------------------* * ********************************************************************* AH ****/ HRESULT CStdSentEnum::InitMorphLexicon( void ) { HRESULT hr = S_OK; m_pMorphLexicon = new CSMorph( m_cpAggregateLexicon, &hr ); return hr; } void CStdSentEnum::fNamesLTS( bool fHaveNamesLTS ) { m_fHaveNamesLTS = fHaveNamesLTS; } // //=== CSentItemEnum ========================================================= // /***************************************************************************** * CSentItemEnum::Next * *---------------------* * ********************************************************************* EDC ***/ STDMETHODIMP CSentItemEnum:: Next( TTSSentItem *pItemEnum ) { SPDBG_FUNC( "CSentItemEnum::Next" ); HRESULT hr = S_OK; //--- Check args if( SPIsBadWritePtr( pItemEnum, sizeof( TTSSentItem ) ) ) { hr = E_INVALIDARG; } else { if ( m_ListPos ) { *pItemEnum = m_ItemList.GetNext( m_ListPos ); } else { hr = S_FALSE; } } return hr; } /* CSentItemEnum::Next */ /***************************************************************************** * CSentItemEnum::Reset * *----------------------* * ********************************************************************* EDC ***/ STDMETHODIMP CSentItemEnum::Reset( void ) { SPDBG_FUNC( "CSentItemEnum::Reset" ); HRESULT hr = S_OK; m_ListPos = m_ItemList.GetHeadPosition(); return hr; } /* CSentItemEnum::Reset */