Visual c++ SAPI 5 TTS事件

Visual c++ SAPI 5 TTS事件,visual-c++,visual-studio-2015,text-to-speech,sapi,Visual C++,Visual Studio 2015,Text To Speech,Sapi,我写信是想就SAPI引擎的一个特殊问题向您提出一些建议。我有一个可以与扬声器和WAV文件对话的应用程序。我还需要注意一些事件,即单词边界和结束输入 m_cpVoice->SetNotifyWindowMessage(m_hWnd, TTS_MSG, 0, 0); hr = m_cpVoice->SetInterest(SPFEI_ALL_EVENTS, SPFEI_ALL_EVENTS); 只是为了测试,我添加了所有事件!当引擎与扬声器通话时,会触发所有事件并将其发

我写信是想就SAPI引擎的一个特殊问题向您提出一些建议。我有一个可以与扬声器和WAV文件对话的应用程序。我还需要注意一些事件,即单词边界和结束输入

    m_cpVoice->SetNotifyWindowMessage(m_hWnd, TTS_MSG, 0, 0);
    hr = m_cpVoice->SetInterest(SPFEI_ALL_EVENTS, SPFEI_ALL_EVENTS);
只是为了测试,我添加了所有事件!当引擎与扬声器通话时,会触发所有事件并将其发送到
m_hWnd
窗口,但当我将输出设置为WAV文件时,不会发送任何事件

    CSpStreamFormat fmt;  
    CComPtr<ISpStreamFormat> pOld;

    m_cpVoice->GetOutputStream(&pOld);
    fmt.AssignFormat(pOld);
    SPBindToFile(file, SPFM_CREATE_ALWAYS, &m_wavStream, &fmt.FormatId(), fmt.WaveFormatExPtr());
    m_cpVoice->SetOutput(m_wavStream, false);
    m_cpVoice->Speak(L"Test", SPF_ASYNC, 0);
csp流格式fmt;
首席执行官波尔德;
m_cpVoice->GetOutputStream(&pOld);
格式(pOld);
SPBindToFile(文件、SPFM_CREATE_ALWAYS、&m_wavStream、&fmt.FormatId()、fmt.WaveFormatExPtr());
m_cpVoice->SetOutput(m_wavStream,false);
m_cpVoice->Speak(L“测试”,SPF_异步,0);
其中,
file
是作为参数传递的路径

实际上,这段代码取自SAPI SDK上的TTS示例。它似乎有点模糊了设置格式的部分

你能帮我找到这个问题吗?或者你们中有谁知道一种更好的将TTS写入WAV的方法吗?我不能使用管理器代码,最好使用C++版本…< /P> 非常感谢你的帮助

编辑1 这似乎是一个线程问题,正在
spuihelp.h
文件中搜索,该文件包含
SPBindToFile
帮助程序,我发现它使用
CoCreateInstance()
函数创建流。这可能是
ISpVoice
对象在其创建线程中失去发送事件的能力的地方


您对此有何看法?

我采用了一种即时解决方案,我认为这种解决方案在大多数情况下都是可以接受的,事实上,当您在文件上编写演讲稿时,您会意识到的主要事件是“停止”事件

所以。。。请看一看类定义:

    #define TTS_WAV_SAVED_MSG            5000
    #define TTS_WAV_ERROR_MSG            5001

    class CSpeech { 
    public:
        CSpeech(HWND); // needed for the notifications
        ...
    private:
        HWND m_hWnd;
        CComPtr<ISpVoice> m_cpVoice;
        ...
        std::thread* m_thread;

        void WriteToWave();
        void SpeakToWave(LPCWSTR, LPCWSTR);
    };
现在。。。查看
WriteToWave()
方法:

    void CSpeech::WriteToWav() {
        // create a new ISpVoice that exists only in this
        // new thread, so we need to 
        //
        // CoInitialize(...) and...
        // CoCreateInstance(...)

        // Now set the voice, i.e. 
        //    rate with global tRate, 
        //    voice token with global pToken
        //    output format and...
        //    bind the stream using tFile as I did in the 
        //      code listed in my question

        cpVoice->Speak(tMsg, SPF_PURGEBEFORESPEAK, 0);
        ...
现在,因为我们没有使用
SPF\u ASYNC
标记,调用被阻塞,但是因为我们在一个单独的线程上,主线程可以继续。
Speak()
方法完成后,新线程可以按如下方式继续:

        ...
        if(/* Speak is went ok */)
            ::PostMessage(tHwn, TTS_WAV_SAVED_MSG, 0, 0);
        else
            ::PostMessage(tHwnd, TTS_WAV_ERROR_MSG, 0, 0);
    }
(***)好的!使用全局变量不是很酷:)但我走得很快。也许使用带有
std::reference\u包装器的线程来传递参数会更优雅

显然,当收到TTS消息时,您需要清理线程以备下次调用!这可以使用如下的
CSpeech::CleanThread()
方法完成:

    void CSpeech::CleanThread() {
        m_thread->join(); // I prefer to be sure the thread has finished!
        delete m_thread;
        m_thread = NULL;
    }

你觉得这个解决方案怎么样?太复杂了?

从发布的代码看不出来,但是当您发送到WAV文件时,您是否在某处发送消息?您是如何编译代码的-Visual Studio?如果是,哪个版本?@EricBrown嗯。。。真的不是。。。我读过,但是“著名的”WaitAndPumpMessage()函数仍然被阻塞(不是吗?@GavinBrelstaff是的,我正在使用社区2015!
    void CSpeech::CleanThread() {
        m_thread->join(); // I prefer to be sure the thread has finished!
        delete m_thread;
        m_thread = NULL;
    }