我正在为微软语音API (SAPI)实现一个ISpTTSEngine。我想要这个声音要像典型的TTS声音一样发音。而不是写my我想委托给内置的ISpVoice。
我已经写了足够多的代码来听到文本的发音,但是它有一个主要的缺陷我还没能解释清楚:演讲要等我说完后才开始ISpTTSEngine:Speak
的实施已经恢复。在…期间声音输出,我的ISpTTSEngine:Speak
实现不调用,甚至使用TTS语音的软件发送请求
(上下文:我这个项目的目标是以编程的方式观察其他片段的语音数据都在试图发声。那部分似乎起了作用目的。)
完整的源代码可用在这里。我会尽力的。总结最相关的部分。
我的ISpTTSEngine
实现有一个私有成员名为m_cpVoice
:
class ATL_NO_VTABLE CTTSEngObj :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<CTTSEngObj, &CLSID_SampleTTSEngine>,
public ISpTTSEngine,
public ISpObjectWithToken
{
// ...
private:
CComPtr<ISpVoice> m_cpVoice;
在FinalConstruct
中初始化方法:
HRESULT CTTSEngObj::FinalConstruct()
{
HRESULT hr = S_OK;
// ...
hr = m_cpVoice.CoCreateInstance(CLSID_SpVoice);
我的ISpTTSEngine:Speak
实现迭代它的文本片段接收并将文本数据传递给ISpVoice::Speak
方法:
STDMETHODIMP CTTSEngObj::Speak(DWORD dwSpeakFlags,
REFGUID rguidFormatId,
const WAVEFORMATEX* pWaveFormatEx,
const SPVTEXTFRAG* pTextFragList,
ISpTTSEngineSite* pOutputSite)
{
// ...
for (const SPVTEXTFRAG* textFrag = pTextFragList; textFrag != NULL; textFrag = textFrag->pNext)
{
// ...
const std::wstring& text = textFrag->pTextStart;
hr = m_cpVoice->Speak(text.substr(0, textFrag->ulTextLen).c_str(), dwSpeakFlags | SPF_ASYNC | SPF_PURGEBEFORESPEAK, 0);
如上所述,直到ISpTTSEngine:Speak
之后才发出音频的回报。一个武断的睡眠陈述最清楚地证明了这一点。轮询ISpVoice的SpeakCompleteEvent
句柄不可避免地超时。删除SPF_ASYNC
标志从ISpVoice::Speak
的调用导致调用者到崩溃。
有人能解释这种行为吗?或者建议我做些改变观察后续的演讲请求?
SAPI不期望递归输入。考虑使用不同的TTS引擎(例如,WinRT System.Media.SpeechSynthesis api)来完成实际的合成。文本片段不会有任何嵌入的标记,所以这不是什么大问题。