/***************************************************************************** * CTTSEngObj::Speak * *-------------------* * Description: * This is the primary method that SAPI calls to render text. *----------------------------------------------------------------------------- * Input Parameters * * pUser * Pointer to the current user profile object. This object contains * information like what languages are being used and this object * also gives access to resources like the SAPI master lexicon object. * * dwSpeakFlags * This is a set of flags used to control the behavior of the * SAPI voice object and the associated engine. * * VoiceFmtIndex * Zero based index specifying the output format that should * be used during rendering. * * pTextFragList * A linked list of text fragments to be rendered. There is * one fragement per XML state change. If the input text does * not contain any XML markup, there will only be a single fragment. * * pOutputSite * The interface back to SAPI where all output audio samples and events are written. * * Return Values * S_OK - This should be returned after successful rendering or if * rendering was interrupted because *pfContinue changed to FALSE. * E_INVALIDARG * E_OUTOFMEMORY * *****************************************************************************/ STDMETHODIMP CTTSEngObj::Speak( DWORD dwSpeakFlags, REFGUID rguidFormatId, const WAVEFORMATEX * pWaveFormatEx, const SPVTEXTFRAG* pTextFragList, ISpTTSEngineSite* pOutputSite ) { SPDBG_FUNC( "CTTSEngObj::Speak" ); HRESULT hr = S_OK; //--- Check args if( SP_IS_BAD_INTERFACE_PTR( pOutputSite ) || SP_IS_BAD_READ_PTR( pTextFragList ) ) { hr = E_INVALIDARG; } else { //--- Init some vars m_pCurrFrag = pTextFragList; m_pNextChar = m_pCurrFrag->pTextStart; m_pEndChar = m_pNextChar + m_pCurrFrag->ulTextLen; m_ullAudioOff = 0; //--- Parse // We've supplied a simple word/sentence breaker just to show one // way of walking the fragment list. It obviously doesn't deal with // things like abreviations and expansion of numbers and dates. CItemList ItemList; while( SUCCEEDED( hr ) && !(pOutputSite->GetActions() & SPVES_ABORT) ) { //--- Do skip? if( pOutputSite->GetActions() & SPVES_SKIP ) { long lSkipCnt; SPVSKIPTYPE eType; hr = pOutputSite->GetSkipInfo( &eType, &lSkipCnt ); if( SUCCEEDED( hr ) ) { //--- Notify SAPI how many items we skipped. We're returning zero // because this feature isn't implemented. hr = pOutputSite->CompleteSkip( 0 ); } } //--- Build the text item list if( SUCCEEDED( hr ) && (hr = GetNextSentence( ItemList )) != S_OK ) { break; } //--- We aren't going to do any part of speech determination, // prosody, or pronunciation determination. If you were, one thing // you will need is access to the SAPI lexicon. You can get that with // the following call. // CComPtr<ISpLexicon> cpLexicon; // hr = pUser->GetLexicon( &cpLexicon ); if( !(pOutputSite->GetActions() & SPVES_ABORT) ) { //--- Fire begin sentence event CSentItem& FirstItem = ItemList.GetHead(); CSentItem& LastItem = ItemList.GetTail(); CSpEvent Event; Event.eEventId = SPEI_SENTENCE_BOUNDARY; Event.elParamType = SPET_LPARAM_IS_UNDEFINED; Event.ullAudioStreamOffset = m_ullAudioOff; Event.lParam = (LPARAM)FirstItem.ulItemSrcOffset; Event.wParam = (WPARAM)LastItem.ulItemSrcOffset + LastItem.ulItemSrcLen - FirstItem.ulItemSrcOffset; hr = pOutputSite->AddEvents( &Event, 1 ); //--- Output if( SUCCEEDED( hr ) ) { hr = OutputSentence( ItemList, pOutputSite ); } } } //--- S_FALSE just says that we hit the end, return okay if( hr == S_FALSE ) { hr = S_OK; } } return hr; } /* CTTSEngObj::Speak */