/***************************************************************************** * CTTSEngObj::OutputSentence * *----------------------------* * This method is used to output an item list. ****************************************************************************/ HRESULT CTTSEngObj::OutputSentence( CItemList& ItemList, ISpTTSEngineSite* pOutputSite ) { HRESULT hr = S_OK; ULONG WordIndex; //--- Lookup words in our voice SPLISTPOS ListPos = ItemList.GetHeadPosition(); while( ListPos && !(pOutputSite->GetActions() & SPVES_ABORT) ) { CSentItem& Item = ItemList.GetNext( ListPos ); //--- Process sentence items switch( Item.pXmlState->eAction ) { //--- Speak some text --------------------------------------- case SPVA_Speak: { //--- We don't say anything for punctuation or control characters // in this sample. if( iswalpha( Item.pItem[0] ) || iswdigit( Item.pItem[0] ) ) { //--- Lookup the word, if we can't find it just use the first one for( WordIndex = 0; WordIndex < m_ulNumWords; ++WordIndex ) { if( ( m_pWordList[WordIndex].ulTextLen == Item.ulItemLen ) && ( !wcsnicmp( m_pWordList[WordIndex].pText, Item.pItem, Item.ulItemLen )) ) { break; } } if( WordIndex == m_ulNumWords ) { WordIndex = 0; } //--- Queue the event CSpEvent Event; Event.eEventId = SPEI_WORD_BOUNDARY; Event.elParamType = SPET_LPARAM_IS_UNDEFINED; Event.ullAudioStreamOffset = m_ullAudioOff; Event.lParam = Item.ulItemSrcOffset; Event.wParam = Item.ulItemSrcLen; hr = pOutputSite->AddEvents( &Event, 1 ); //--- Queue the audio data hr = pOutputSite->Write( m_pWordList[WordIndex].pAudio, m_pWordList[WordIndex].ulNumAudioBytes, NULL ); //--- Update the audio offset m_ullAudioOff += m_pWordList[WordIndex].ulNumAudioBytes; } } break; //--- Output some silence for a pause ----------------------- case SPVA_Silence: { BYTE Buff[1000]; memset( Buff, 0, 1000 ); ULONG NumSilenceBytes = Item.pXmlState->SilenceMSecs * 22; //--- Queue the audio data in chunks so that we can get // interrupted if necessary. while( !(pOutputSite->GetActions() & SPVES_ABORT) ) { if( NumSilenceBytes > 1000 ) { hr = pOutputSite->Write( Buff, 1000, NULL ); NumSilenceBytes -= 1000; } else { hr = pOutputSite->Write( Buff, NumSilenceBytes, NULL ); break; } } //--- Update the audio offset m_ullAudioOff += NumSilenceBytes; } break; //--- Fire a bookmark event --------------------------------- case SPVA_Bookmark: { //--- The bookmark is NOT a null terminated string in the Item, but we need //--- to convert it to one. Allocate enough space for the string. WCHAR * pszBookmark = (WCHAR *)_alloca((Item.ulItemLen + 1) * sizeof(WCHAR)); memcpy(pszBookmark, Item.pItem, Item.ulItemLen * sizeof(WCHAR)); pszBookmark[Item.ulItemLen] = 0; //--- Queue the event SPEVENT Event; Event.eEventId = SPEI_TTS_BOOKMARK; Event.elParamType = SPET_LPARAM_IS_STRING; Event.ullAudioStreamOffset = m_ullAudioOff; Event.lParam = (LPARAM)pszBookmark; Event.wParam = _wtol(pszBookmark); hr = pOutputSite->AddEvents( &Event, 1 ); } break; case SPVA_Pronounce: //--- Our sample engine doesn't handle this. If it // did, you would use the associated pronunciation in // the XmlState structure instead of the lexicon. break; case SPVA_ParseUnknownTag: //--- This will reference an XML tag that is unknown to SAPI // if your engine has private tags to control state, you // would examine these tags and see if you recognize it. This // would also be the point that you would make the rendering // state change. break; } } return hr; } /* CTTSEngObj::OutputSentence */