首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >SAPI 5 TTS事件

SAPI 5 TTS事件
EN

Stack Overflow用户
提问于 2016-04-02 16:55:26
回答 1查看 415关注 0票数 2

我写信是想问你一些关于SAPI引擎的特殊问题的建议。我有一个应用程序,可以同时与扬声器和WAV文件。我还需要了解一些事件,即单词边界和结束输入。

代码语言:javascript
复制
    m_cpVoice->SetNotifyWindowMessage(m_hWnd, TTS_MSG, 0, 0);
    hr = m_cpVoice->SetInterest(SPFEI_ALL_EVENTS, SPFEI_ALL_EVENTS);

只是为了测试,我添加了所有事件!当引擎与扬声器对话时,所有事件都被触发并发送到m_hWnd窗口,但当我将输出设置为WAV文件时,它们都不会被发送。

代码语言:javascript
复制
    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);

其中file是作为参数传递的路径。

实际上,这段代码是从SAPI SDK上找到的TTS示例中提取的。似乎有点模糊的部分设置格式..。

你能帮我找出这个问题吗?或者你们中有谁知道如何更好地将TTS写到WAV上?我不能使用管理器代码,最好使用C++版本.

非常感谢你的帮助

编辑1

这似乎是一个线程问题,并在spuihelp.h文件中进行搜索,其中包含我发现的SPBindToFile助手,它使用CoCreateInstance()函数创建流。也许这就是ISpVoice对象失去在其创建线程中发送事件的能力的地方。

你对此有什么看法?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-04-06 14:39:51

我采用了一个即时解决方案,我认为在大多数情况下应该是可以接受的,事实上,当你在文件上写演讲稿时,你会注意到的主要事件是“停止”事件。

所以..。看看一个类的定义:

代码语言:javascript
复制
    #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);
    };

我按照以下方式实现了方法SpeakToWav

代码语言:javascript
复制
    // Global variables (***)
    LPCWSTR tMsg;
    LPCWSTR tFile;
    long tRate;
    HWND tHwnd;
    ISpObjectToken* pToken;

    void CSpeech::SpeakToWave(LPCWSTR file, LPCWSTR msg) {
        // Using, for example wcscpy_s:
        // tMsg <- msg;
        // tFile <- file;

        tHwnd = m_hWnd;
        m_cpVoice->GetRate(&tRate);
        m_cpVoice->GetVoice(&pToken);

        if(m_thread == NULL)
            m_thread = new std::thread(&CSpeech::WriteToWave, this);
    }

而现在..。请看一下WriteToWave()方法:

代码语言:javascript
复制
    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_ASYNC标志,所以调用被阻塞了,但是因为我们在一个单独的线程上,主线程可以继续。在Speak()方法完成之后,新线程可以继续如下:

代码语言:javascript
复制
        ...
        if(/* Speak is went ok */)
            ::PostMessage(tHwn, TTS_WAV_SAVED_MSG, 0, 0);
        else
            ::PostMessage(tHwnd, TTS_WAV_ERROR_MSG, 0, 0);
    }

(*)好的!使用全局变量不是很酷:)但是我走得很快。也许使用带有std::reference_wrapper的线程来传递参数会更优雅!

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

代码语言:javascript
复制
    void CSpeech::CleanThread() {
        m_thread->join(); // I prefer to be sure the thread has finished!
        delete m_thread;
        m_thread = NULL;
    }

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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36376131

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档