Delphi 11调用OneCore SAPI TTS时语音卡顿及语速无法修改问题
问题1:高负载下语音播放卡顿
核心原因
音乐播放器能流畅播放是因为它们会将音频会话标记为高优先级,避免系统在高负载时节流音频线程。你的TTS默认使用普通优先级线程,容易被其他进程抢占CPU时间。单纯将Speak()放入线程无法解决,因为线程优先级未针对性调整,且未配置音频会话优先级。
解决步骤
提升音频会话优先级
Windows Audio Session API允许为音频流设置优先级,确保TTS音频不被系统节流。需导入IAudioSessionControl2接口并配置:type IAudioSessionControl2 = interface(IAudioSessionControl) ['{bfb7ff88-7239-4fc9-8fa2-07c950be9c6d}'] function SetSessionPriority(priority: LPCWSTR): HRESULT; stdcall; // 其他接口方法需完整导入,此处省略 end; procedure SetTTSSessionPriority; var pDeviceEnumerator: IMMDeviceEnumerator; pDefaultDevice: IMMDevice; pAudioSessionManager: IAudioSessionManager2; pSessionControl: IAudioSessionControl; pSessionControl2: IAudioSessionControl2; begin if CoCreateInstance(CLSID_MMDeviceEnumerator, nil, CLSCTX_ALL, IID_IMMDeviceEnumerator, pDeviceEnumerator) = S_OK then begin if pDeviceEnumerator.GetDefaultAudioEndpoint(eRender, eMultimedia, pDefaultDevice) = S_OK then begin if pDefaultDevice.Activate(IID_IAudioSessionManager2, CLSCTX_ALL, nil, pAudioSessionManager) = S_OK then begin if pAudioSessionManager.GetAudioSessionControl(nil, 0, pSessionControl) = S_OK then begin if pSessionControl.QueryInterface(IID_IAudioSessionControl2, pSessionControl2) = S_OK then begin pSessionControl2.SetSessionPriority('Pro Audio'); pSessionControl2.Release; end; pSessionControl.Release; end; pAudioSessionManager.Release; end; pDefaultDevice.Release; end; pDeviceEnumerator.Release; end; end;调整TTS线程优先级
处理TTS播放的线程需设置为高于普通优先级,避免被BLE数据处理等任务抢占:// 在TTS线程的Execute方法开头设置优先级 SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_ABOVE_NORMAL);注意:仅在播放期间提升优先级,播放完成后恢复原优先级,避免影响系统整体性能。
使用异步播放并避免阻塞
调用Speak时使用SPF_ASYNC参数,让SAPI在后台处理播放,不阻塞主线程或BLE处理线程:tts.Speak(s, SPF_ASYNC or SPF_PURGEBEFORESPEAK);同时实现
ISpNotifySink接口监听播放完成事件,避免重复调用导致队列拥堵。隔离BLE数据处理与TTS资源
将BLE数据接收和处理放入低优先级线程,或使用线程安全队列缓冲数据,避免在接收回调中执行耗时操作,减少与TTS线程的CPU竞争。
问题2:SetRate无法修改语速
核心原因
OneCore SAPI的语速配置逻辑与传统SAPI略有差异,直接调用ISpVoice::SetRate可能未正确作用于当前使用的语音引擎,或参数范围与系统GUI的设置不匹配。
解决步骤
确认参数范围匹配
OneCore语音引擎的语速范围可能与传统SAPI的-1010不同,部分中文引擎支持-50100的范围。先获取当前语速值,再调整:var CurrentRate: Integer; begin tts.GetRate(CurrentRate); // 测试不同范围的数值,比如设置为5(对应系统GUI的中等偏快) tts.SetRate(5); end;通过语音Token配置语速
直接修改语音引擎的属性,确保设置生效:var pVoiceToken: ISpObjectToken; begin if tts.GetVoice(pVoiceToken) = S_OK then begin pVoiceToken.SetStringValue('Attributes', 'Rate', '5'); // 重新加载语音配置 tts.SetVoice(pVoiceToken); pVoiceToken.Release; end; end;确保设置时机正确
必须在调用Speak之前调用SetRate,如果使用异步播放,需在上一次播放完成后再设置下一次的语速,避免被队列中的任务覆盖。检查OneCore引擎兼容性
部分OneCore中文语音引擎需要特定的初始化顺序,确保在创建ISpVoice实例后,先选择语音引擎再设置语速:var pVoiceToken: ISpObjectToken; begin // 查找OneCore中文语音引擎 if SpFindBestToken(SPCAT_VOICES, 'Language=804;Vendor=Microsoft', nil, pVoiceToken) = S_OK then begin tts.SetVoice(pVoiceToken); tts.SetRate(3); // 设置语速 pVoiceToken.Release; end; end;
内容的提问来源于stack exchange,提问作者Xiao Jack

