我正在为 Microsoft Speech API (SAPI) 实现 ISpTTSEngine。我想要 这个声音就像典型的 TTS 声音一样进行报读。而不是写我的 自己的语音合成器,我想委托给内置的 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
之后才发出音频 返回。任意的 sleep 语句最清楚地证明了这一点。轮询 ISpVoice 的
SpeakCompleteEvent
句柄不可避免地会超时。删除 调用
SPF_ASYNC
时的
ISpVoice::Speak
标志会导致调用者 崩溃。任何人都可以解释这种行为吗?或者提出一个改变,让我能够 观察后续的语音请求?
System.Media.SpeechSynthesis API)来进行实际合成。文本片段不会有任何嵌入的标记,所以这不是什么大问题。