我写信是想问你一些关于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);其中file是作为参数传递的路径。
实际上,这段代码是从SAPI SDK上找到的TTS示例中提取的。似乎有点模糊的部分设置格式..。
你能帮我找出这个问题吗?或者你们中有谁知道如何更好地将TTS写到WAV上?我不能使用管理器代码,最好使用C++版本.
非常感谢你的帮助
编辑1
这似乎是一个线程问题,并在spuihelp.h文件中进行搜索,其中包含我发现的SPBindToFile助手,它使用CoCreateInstance()函数创建流。也许这就是ISpVoice对象失去在其创建线程中发送事件的能力的地方。
你对此有什么看法?
发布于 2016-04-06 14:39:51
我采用了一个即时解决方案,我认为在大多数情况下应该是可以接受的,事实上,当你在文件上写演讲稿时,你会注意到的主要事件是“停止”事件。
所以..。看看一个类的定义:
#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
// 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()方法:
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()方法完成之后,新线程可以继续如下:
...
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()方法来完成:
void CSpeech::CleanThread() {
m_thread->join(); // I prefer to be sure the thread has finished!
delete m_thread;
m_thread = NULL;
}你觉得这个解决方案怎么样?太复杂了?
https://stackoverflow.com/questions/36376131
复制相似问题