You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SAPI实现ISpTTSEngine调用ISpVoice发音延迟及请求接收异常问题

问题原因解析
  1. 线程消息泵阻塞导致播放延迟
    你在ISpTTSEngine实例的初始化线程(也就是FinalConstruct执行的线程)中创建了ISpVoice对象,SAPI的ISpVoice默认依赖当前线程的消息队列驱动异步播放逻辑。而ISpTTSEngine::Speak方法被调用时,当前线程是SAPI的引擎回调线程,在该方法返回前,线程的消息循环处于阻塞状态,ISpVoice的播放逻辑无法执行,所以只能等Speak方法返回后才会开始输出音频。
  2. SAPI串行调度导致新请求无法进入
    SAPI对ISpTTSEngine的调用默认是串行的:上一次ISpTTSEngine::Speak调用未返回前,SAPI不会下发新的播报请求。同时你当前的实现逻辑里,ISpTTSEngine作为服务提供方,没有向上层SAPI返回合成的音频数据,也没有通知SAPI当前请求已处理完成,SAPI会一直认为当前请求还在处理中,自然不会下发新的请求。
  3. 其他异常现象的原因
  • 去掉SPF_ASYNC标记后调用ISpVoice::Speak会触发同步播放,会一直阻塞到播放完成才返回,超出SAPI引擎回调的超时限制后就会被系统强制终止,表现为调用方崩溃。
  • 在ISpTTSEngine::Speak方法内轮询SpeakCompleteEvent时,当前线程被轮询逻辑占满,ISpVoice的播放逻辑无法拿到CPU执行,自然永远不会触发完成事件,所以一直超时。
修改方案

根据你的需求(监听其他软件的播报文本),可以采用以下两种方案:

方案1:仅需要监听文本,不需要向调用方返回合成音频

  • 将内部ISpVoice的创建、播放逻辑全部移到独立的工作线程中,工作线程单独初始化COM环境(调用CoInitializeEx(COINIT_APARTMENTTHREADED))并运行消息循环,不要占用SAPI的引擎回调线程。
  • 在ISpTTSEngine::Speak方法中,仅需要把收到的文本片段投递到工作线程的任务队列,之后直接返回S_OK即可,不需要等待播放完成。这样SAPI会认为当前请求已处理完成,后续有新请求就会正常调用你的Speak实现。

方案2:需要让引擎表现和常规TTS一致,调用方可正常获取音频

  • 不要让内部ISpVoice直接输出到音频设备,将其输出绑定到ISpStream流对象,等ISpVoice把合成的音频写入流后,你再通过ISpTTSEngineSite::Write方法把音频数据回写给上层SAPI。
  • 同步处理ISpTTSEngineSite抛出的停止、暂停等事件,收到对应事件时调用内部ISpVoice的对应方法中断或暂停当前合成任务。

内容的提问来源于stack exchange,提问作者Mike

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 04:36:03