Visual c++ SAPI 5 TTS事件
我写信是想就SAPI引擎的一个特殊问题向您提出一些建议。我有一个可以与扬声器和WAV文件对话的应用程序。我还需要注意一些事件,即单词边界和结束输入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); 只是为了测试,我添加了所有事件!当引擎与扬声器通话时,会触发所有事件并将其发
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;
}