Android双语言TTS同步难题:TalkBack创建引擎的回调获取方案
双语言TTS引擎同步问题解决方案验证(面向视障Android项目)
问题背景
我有一个面向视障用户的Android项目,需要组合两款文本转语音(TTS)引擎,核心困境是主TTS引擎实例由Google TalkBack创建时,无法同步两款引擎的输出。
这款主引擎是双语言引擎,会识别输入文本的语言并调用对应引擎合成语音,其中英文引擎支持从系统可用引擎中选择。原本可以通过UtteranceProgressListener的onDone方法监听语音播放完成,但由于主引擎由TalkBack创建,我无法在主程序中调用setOnUtteranceCompletedListener设置回调,导致无法获取主引擎的播放完成事件——目前只能依赖次引擎的onDone实现英文语音在波斯语之后播放,完全走不通。
三种思路的可行性验证
1. AccessibilityService是否允许访问TalkBack创建的TextToSpeech对象或语音播放完成事件?
- 不可行。Android的AccessibilityService权限无法直接获取TalkBack内部创建的
TextToSpeech实例,也没有公开API能监听TalkBack发起的TTS播放完成事件。AccessibilityService仅能获取界面元素变化、通知等系统级辅助事件,无法穿透到TalkBack的内部TTS实例状态。
2. 构建嵌套TTS引擎:主引擎仅做文本语言拆分,调用另外两个引擎合成,但每个包仅能实现一个TextToSpeechService,需重写TextToSpeech类创建嵌套实例,该方案是否可行?
- 可行,但无需重写
TextToSpeech类:- 虽然每个包只能注册一个
TextToSpeechService作为系统TTS引擎,但你可以在自己的TtsService内部直接创建多个TextToSpeech实例,嵌套调用其他系统TTS引擎。 - 在你的
TtsService中初始化其他引擎的TextToSpeech对象,通过setOnUtteranceProgressListener监听它们的合成/播放状态,自行管理播放队列的顺序即可。 - 注意事项:嵌套的
TextToSpeech实例需要在onDestroy时正确释放,避免内存泄漏;同时要处理引擎初始化失败的情况(比如用户选择的英文引擎未安装)。
- 虽然每个包只能注册一个
3. 利用UtteranceProgressListener的onAudioAvailable获取合成音频缓冲区,重采样后写入主引擎缓冲区,但onAudioAvailable由次引擎的同步线程调用,导致无法播放,是否可通过管道让主同步线程读写缓冲区来解决?
- 可行,但实现复杂度较高:
- 当次引擎的
onAudioAvailable被调用时,将音频数据写入线程安全的管道(如PipeOutputStream),然后在主引擎的合成回调线程中从PipeInputStream读取数据,通过SynthesisCallback的audioAvailable方法输出。 - 需要统一音频格式(重采样、声道、采样率需匹配主引擎要求),避免播放时出现杂音或速度异常。
- 线程同步:
onAudioAvailable由次引擎的同步线程调用,管道读写需确保线程安全,可使用阻塞式管道或带缓冲区的队列协调。
- 当次引擎的
核心代码片段
public class TtsService extends TextToSpeechService { private static final String TAG = TtsService.class.getSimpleName(); private SpeechSynthesis mEngine; private SynthesisCallback mCallback; private static final boolean DEBUG = false; private static final String DEFAULT_LANGUAGE_1 = "fas"; private static final String DEFAULT_COUNTRY_1 = "IRN"; private static final String DEFAULT_VARIANT_1 = ""; // English配置 private static final String DEFAULT_LANGUAGE_2 = "eng"; private static final String DEFAULT_COUNTRY_2 = "USA"; private static final String DEFAULT_VARIANT_2 = ""; private String mLanguage = DEFAULT_LANGUAGE_1; private String mCountry = DEFAULT_COUNTRY_1; private String mVariant = DEFAULT_VARIANT_1; // 省略其他代码... }
内容的提问来源于stack exchange,提问作者Majid Namnabat
相关产品推荐
相关产品推荐

