当“event.GetFrom(m_cpVoice)==S_OK”时调用函数(因此,当事件发生时)[SAPI 5.1 和 C++]

发布于 2024-09-02 06:43:28 字数 265 浏览 2 评论 0原文

我正在做一个带有 3D 模型的项目,这说明了这一点。 因此,我正在使用 SAPI 5.1,并且我想在存在 Viseme 事件时异步调用函数(以便播放相关的动画)。

我怎样才能做到呢?

非常感谢。

注意:我使用: hRes = m_cpVoice->Speak(L"All I想要的是解决这个问题", SPF_ASYNC , NULL); 我知道 CspEvent, event.eEventId 。我想要的只是当 Sapi 事件发生时如何调用函数

I'm doing a project with a 3D model, that speaks.
So, I'm using SAPI 5.1, and I want to call a function asynchronously when there's a Viseme event (in order to play the animation related to).

How could I do it?

Thank you very much.

Note: I use : hRes = m_cpVoice->Speak(L"All I want is to solve this problem", SPF_ASYNC , NULL);
And I know the CspEvent, event.eEventId . All I want is how to call a function when Sapi event happens

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

惟欲睡 2024-09-09 06:43:28

首先,您需要调用 m_cpVoice->SetInterest (SPFEI(SPEI_VISEME), SPFEI(SPEI_VISEME));这将告诉 SAPI 在 VISEME 事件触发时发送事件。

其次,您需要通过调用 m_cpVoice->SetNotifyCallbackInterface,带有您的回调。 (它必须实现 ISpNotifyCallback,其中是您的对象将实现的虚拟 C++ 接口。)

您可以查看 SAPI 事件文档 了解更多详细信息。

ISpNotifyCallback 的示例实现如下所示:

TTSHandler.h:

class CTTSHandler : ISpNotifyCallback
{
public:
    CTTSHandler(void);
    ~CTTSHandler(void);
    HRESULT Initialize();
    HRESULT DoSpeak();
    HRESULT Uninitialize();

private:
    HRESULT STDMETHODCALLTYPE NotifyCallback(WPARAM wParam, LPARAM lParam);
    void TTSAppStatusMessage(LPCTSTR str);

    CComPtr<ISpAudio>   m_cpOutAudio;
    CComPtr<ISpVoice> m_cpVoice;
    HANDLE m_hSpeakDone;
};

TTSHandler.cpp:

#include "TTSHandler.h"
#include <sphelper.h>

CTTSHandler::CTTSHandler(void) : m_hSpeakDone(INVALID_HANDLE_VALUE)
{
}

CTTSHandler::~CTTSHandler(void)
{
}

HRESULT CTTSHandler::Initialize()
{
    HRESULT hr = m_cpVoice.CoCreateInstance( CLSID_SpVoice );
    if ( SUCCEEDED( hr ) )
    {
        SpCreateDefaultObjectFromCategoryId( SPCAT_AUDIOOUT, &m_cpOutAudio );
    }
    if( SUCCEEDED( hr ) )
    {
        hr = m_cpVoice->SetOutput( m_cpOutAudio, FALSE );
    }
    if ( SUCCEEDED( hr ) )
    {
        hr = m_cpVoice->SetNotifyCallbackInterface(this, 0, 0);
    }
    // We're interested in all TTS events
    if( SUCCEEDED( hr ) )
    {
        hr = m_cpVoice->SetInterest( SPFEI_ALL_TTS_EVENTS, SPFEI_ALL_TTS_EVENTS );
    }
    if (SUCCEEDED(hr))
    {
        m_hSpeakDone = ::CreateEvent(NULL, TRUE, FALSE, NULL);     // anonymous event used to wait for speech done
    }
    return hr;
}

HRESULT CTTSHandler::DoSpeak()
{
    HRESULT hr = m_cpVoice->Speak( L"This is a reasonably long string that should take a while to speak.  This is some more text.", SPF_ASYNC |SPF_IS_NOT_XML, 0 );
    if (FAILED(hr))
    {
        TTSAppStatusMessage(  _T("speak failed\r\n") );
    }
    else
    {
        BOOL fContinue = TRUE;
        while (fContinue)
        {
            DWORD dwWaitId = ::MsgWaitForMultipleObjectsEx(1, &m_hSpeakDone, INFINITE, QS_ALLINPUT, MWMO_INPUTAVAILABLE);
            switch (dwWaitId)
            {
            case WAIT_OBJECT_0:
                {
                    fContinue = FALSE;
                }
                break;

            case WAIT_OBJECT_0 + 1:
                {
                    MSG Msg;
                    while (::PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
                    {
                        ::TranslateMessage(&Msg);
                        ::DispatchMessage(&Msg);
                    }
                }
                break;

            case WAIT_TIMEOUT:
                {
                    hr = S_FALSE;
                    fContinue = FALSE;
                }
                break;

            default:// Unexpected error
                {
                    TTSAppStatusMessage(L"Unexpected error returned from MsgWaitForMultipleObj");
                    hr = HRESULT_FROM_WIN32(::GetLastError());
                    fContinue = FALSE;
                }
                break;
            }
        }
    }
    return hr;
}

HRESULT CTTSHandler::Uninitialize()
{
    m_cpVoice = NULL;
    return S_OK;
}


void CTTSHandler::TTSAppStatusMessage(LPCTSTR szMessage )
{
    wprintf_s(L"%s", szMessage);
}

/////////////////////////////////////////////////////////////////
HRESULT STDMETHODCALLTYPE CTTSHandler::NotifyCallback(WPARAM, LPARAM)
/////////////////////////////////////////////////////////////////
//
// Handles the WM_TTSAPPCUSTOMEVENT application defined message and all
// of it's appropriate SAPI5 events.
//
{

    CSpEvent        event;  // helper class in sphelper.h for events that releases any 
    // allocated memory in it's destructor - SAFER than SPEVENT
    int             i = 0;
    HRESULT         hr = S_OK;

    while( event.GetFrom(m_cpVoice) == S_OK )
    {
        switch( event.eEventId )
        {
        case SPEI_START_INPUT_STREAM:
            TTSAppStatusMessage(  _T("StartStream event\r\n") );
            break; 

        case SPEI_END_INPUT_STREAM:
            TTSAppStatusMessage(  _T("EndStream event\r\n") );
            SetEvent(m_hSpeakDone);
            break;     

        case SPEI_VOICE_CHANGE:
            TTSAppStatusMessage(  _T("Voicechange event\r\n") );
            break;

        case SPEI_TTS_BOOKMARK:
            {
                // Get the string associated with the bookmark
                // and add the null terminator.
                TCHAR szBuff2[MAX_PATH] = _T("Bookmark event: ");

                size_t cEventString = wcslen( event.String() ) + 1;
                WCHAR *pwszEventString = new WCHAR[ cEventString ];
                if ( pwszEventString )
                {
                    wcscpy_s( pwszEventString, cEventString, event.String() );
                    _tcscat_s( szBuff2, _countof(szBuff2), CW2T(pwszEventString) );
                    delete[] pwszEventString;
                }

                _tcscat_s( szBuff2, _countof(szBuff2), _T("\r\n") );
                TTSAppStatusMessage(  szBuff2 );
            }
            break;

        case SPEI_WORD_BOUNDARY:
            TTSAppStatusMessage(  _T("Wordboundary event\r\n") );
            break;

        case SPEI_PHONEME:
            TTSAppStatusMessage(  _T("Phoneme event\r\n") );
            break;

        case SPEI_VISEME:
            TTSAppStatusMessage(  _T("Viseme event\r\n") );
            break;

        case SPEI_SENTENCE_BOUNDARY:
            TTSAppStatusMessage(  _T("Sentence event\r\n") );
            break;

        case SPEI_TTS_AUDIO_LEVEL:
            WCHAR wszBuff[MAX_PATH];
            swprintf_s(wszBuff, _countof(wszBuff), L"Audio level: %d\r\n", (ULONG)event.wParam);
            TTSAppStatusMessage(  CW2T(wszBuff) );
            break;

        case SPEI_TTS_PRIVATE:
            TTSAppStatusMessage(  _T("Private engine event\r\n") );
            break;

        default:
            TTSAppStatusMessage(  _T("Unknown message\r\n") );
            break;
        }
    }
    return hr;
}

First, you need to call m_cpVoice->SetInterest(SPFEI(SPEI_VISEME), SPFEI(SPEI_VISEME)); that will tell SAPI to send an event when a VISEME event fires.

Second, you need to set up an event handler by calling m_cpVoice->SetNotifyCallbackInterface, with your callback. (It must implement ISpNotifyCallback, which is a virtual C++ interface that your object would implement.)

You can look at the SAPI events documentation for more details.

A sample implementation of ISpNotifyCallback would look like this:

TTSHandler.h:

class CTTSHandler : ISpNotifyCallback
{
public:
    CTTSHandler(void);
    ~CTTSHandler(void);
    HRESULT Initialize();
    HRESULT DoSpeak();
    HRESULT Uninitialize();

private:
    HRESULT STDMETHODCALLTYPE NotifyCallback(WPARAM wParam, LPARAM lParam);
    void TTSAppStatusMessage(LPCTSTR str);

    CComPtr<ISpAudio>   m_cpOutAudio;
    CComPtr<ISpVoice> m_cpVoice;
    HANDLE m_hSpeakDone;
};

TTSHandler.cpp:

#include "TTSHandler.h"
#include <sphelper.h>

CTTSHandler::CTTSHandler(void) : m_hSpeakDone(INVALID_HANDLE_VALUE)
{
}

CTTSHandler::~CTTSHandler(void)
{
}

HRESULT CTTSHandler::Initialize()
{
    HRESULT hr = m_cpVoice.CoCreateInstance( CLSID_SpVoice );
    if ( SUCCEEDED( hr ) )
    {
        SpCreateDefaultObjectFromCategoryId( SPCAT_AUDIOOUT, &m_cpOutAudio );
    }
    if( SUCCEEDED( hr ) )
    {
        hr = m_cpVoice->SetOutput( m_cpOutAudio, FALSE );
    }
    if ( SUCCEEDED( hr ) )
    {
        hr = m_cpVoice->SetNotifyCallbackInterface(this, 0, 0);
    }
    // We're interested in all TTS events
    if( SUCCEEDED( hr ) )
    {
        hr = m_cpVoice->SetInterest( SPFEI_ALL_TTS_EVENTS, SPFEI_ALL_TTS_EVENTS );
    }
    if (SUCCEEDED(hr))
    {
        m_hSpeakDone = ::CreateEvent(NULL, TRUE, FALSE, NULL);     // anonymous event used to wait for speech done
    }
    return hr;
}

HRESULT CTTSHandler::DoSpeak()
{
    HRESULT hr = m_cpVoice->Speak( L"This is a reasonably long string that should take a while to speak.  This is some more text.", SPF_ASYNC |SPF_IS_NOT_XML, 0 );
    if (FAILED(hr))
    {
        TTSAppStatusMessage(  _T("speak failed\r\n") );
    }
    else
    {
        BOOL fContinue = TRUE;
        while (fContinue)
        {
            DWORD dwWaitId = ::MsgWaitForMultipleObjectsEx(1, &m_hSpeakDone, INFINITE, QS_ALLINPUT, MWMO_INPUTAVAILABLE);
            switch (dwWaitId)
            {
            case WAIT_OBJECT_0:
                {
                    fContinue = FALSE;
                }
                break;

            case WAIT_OBJECT_0 + 1:
                {
                    MSG Msg;
                    while (::PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
                    {
                        ::TranslateMessage(&Msg);
                        ::DispatchMessage(&Msg);
                    }
                }
                break;

            case WAIT_TIMEOUT:
                {
                    hr = S_FALSE;
                    fContinue = FALSE;
                }
                break;

            default:// Unexpected error
                {
                    TTSAppStatusMessage(L"Unexpected error returned from MsgWaitForMultipleObj");
                    hr = HRESULT_FROM_WIN32(::GetLastError());
                    fContinue = FALSE;
                }
                break;
            }
        }
    }
    return hr;
}

HRESULT CTTSHandler::Uninitialize()
{
    m_cpVoice = NULL;
    return S_OK;
}


void CTTSHandler::TTSAppStatusMessage(LPCTSTR szMessage )
{
    wprintf_s(L"%s", szMessage);
}

/////////////////////////////////////////////////////////////////
HRESULT STDMETHODCALLTYPE CTTSHandler::NotifyCallback(WPARAM, LPARAM)
/////////////////////////////////////////////////////////////////
//
// Handles the WM_TTSAPPCUSTOMEVENT application defined message and all
// of it's appropriate SAPI5 events.
//
{

    CSpEvent        event;  // helper class in sphelper.h for events that releases any 
    // allocated memory in it's destructor - SAFER than SPEVENT
    int             i = 0;
    HRESULT         hr = S_OK;

    while( event.GetFrom(m_cpVoice) == S_OK )
    {
        switch( event.eEventId )
        {
        case SPEI_START_INPUT_STREAM:
            TTSAppStatusMessage(  _T("StartStream event\r\n") );
            break; 

        case SPEI_END_INPUT_STREAM:
            TTSAppStatusMessage(  _T("EndStream event\r\n") );
            SetEvent(m_hSpeakDone);
            break;     

        case SPEI_VOICE_CHANGE:
            TTSAppStatusMessage(  _T("Voicechange event\r\n") );
            break;

        case SPEI_TTS_BOOKMARK:
            {
                // Get the string associated with the bookmark
                // and add the null terminator.
                TCHAR szBuff2[MAX_PATH] = _T("Bookmark event: ");

                size_t cEventString = wcslen( event.String() ) + 1;
                WCHAR *pwszEventString = new WCHAR[ cEventString ];
                if ( pwszEventString )
                {
                    wcscpy_s( pwszEventString, cEventString, event.String() );
                    _tcscat_s( szBuff2, _countof(szBuff2), CW2T(pwszEventString) );
                    delete[] pwszEventString;
                }

                _tcscat_s( szBuff2, _countof(szBuff2), _T("\r\n") );
                TTSAppStatusMessage(  szBuff2 );
            }
            break;

        case SPEI_WORD_BOUNDARY:
            TTSAppStatusMessage(  _T("Wordboundary event\r\n") );
            break;

        case SPEI_PHONEME:
            TTSAppStatusMessage(  _T("Phoneme event\r\n") );
            break;

        case SPEI_VISEME:
            TTSAppStatusMessage(  _T("Viseme event\r\n") );
            break;

        case SPEI_SENTENCE_BOUNDARY:
            TTSAppStatusMessage(  _T("Sentence event\r\n") );
            break;

        case SPEI_TTS_AUDIO_LEVEL:
            WCHAR wszBuff[MAX_PATH];
            swprintf_s(wszBuff, _countof(wszBuff), L"Audio level: %d\r\n", (ULONG)event.wParam);
            TTSAppStatusMessage(  CW2T(wszBuff) );
            break;

        case SPEI_TTS_PRIVATE:
            TTSAppStatusMessage(  _T("Private engine event\r\n") );
            break;

        default:
            TTSAppStatusMessage(  _T("Unknown message\r\n") );
            break;
        }
    }
    return hr;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文