You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

TTS播放暂停功能开发求助:停止语音后循环无法终止

解决TTS播放暂停与循环终止问题

我看了你遇到的问题:想给TTS加暂停/播放功能,但暂停时循环停不下来,也没法准确获取最后播放的词汇。核心问题在于你的同步循环+异步TTS调用的矛盾,还有没正确用标志位中断循环。咱们一步步来修复:

问题分析

  • 同步循环阻塞,无法响应暂停speakSpeech里的while循环是同步执行的,哪怕你设置了keepGoing=false,循环还是会把所有词汇都塞进TTS队列,根本停不下来——因为循环没在每次迭代时检查这个标志。
  • TTS异步调用无法跟踪播放位置:你一次性把所有词汇都加入播放队列,没法知道当前正在播放哪一个,暂停时自然没法准确记录最后播放的位置。
  • 暂停逻辑冗余toggleAudioMode里调用speakSpeech(0, "stop")完全没必要,直接停止TTS就行,而且这个调用里的循环还会多跑一遍。

修复方案

我们需要改成逐个播放词汇,用TTS的UtteranceProgressListener监听每个词汇的播放完成事件,同时在播放流程中检查keepGoing标志,这样就能随时中断播放,也能准确记录当前位置。

修改后的完整代码示例

private TextToSpeech textToSpeech;
private boolean isPlayerVisible = false;
private boolean keepGoing = true;
private String[] splitspeech;
private int currentIndex = 0; // 记录当前播放的词汇索引
private HashMap<String, String> myHash = new HashMap<>();

public void initTTS() {
    // 初始化TTS时设置监听器
    textToSpeech = new TextToSpeech(context, status -> {
        if (status == TextToSpeech.SUCCESS) {
            // 设置语言等初始化操作...
            // 添加Utterance监听器,跟踪每个词汇的播放完成
            textToSpeech.setOnUtteranceProgressListener(new UtteranceProgressListener() {
                @Override
                public void onStart(String utteranceId) {}

                @Override
                public void onDone(String utteranceId) {
                    // 当前词汇播放完成,继续播放下一个(如果keepGoing为true)
                    if (keepGoing && currentIndex < splitspeech.length - 1) {
                        currentIndex++;
                        speakNextWord();
                    }
                }

                @Override
                public void onError(String utteranceId) {}
            });
        }
    });
}

public void toggleAudioMode() {
    isPlayerVisible = !isPlayerVisible;
    ivToggleAudioModeAnswer.setImageResource(isPlayerVisible ? R.drawable.ic_audio_off : R.drawable.ic_audio_on);
    
    if (isPlayerVisible) {
        // 开始/继续播放
        keepGoing = true;
        if (splitspeech == null) {
            splitspeech = result.split(" ");
        }
        speakNextWord();
    } else {
        // 暂停播放
        keepGoing = false;
        textToSpeech.stop();
        // 这里currentIndex就是最后播放到的词汇索引
    }
}

private void speakNextWord() {
    if (!keepGoing || currentIndex >= splitspeech.length) {
        return;
    }
    myHash.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "utterance_" + currentIndex);
    // 每次播放单个词汇,用QUEUE_FLUSH清空之前的队列(避免暂停后残留)
    textToSpeech.speak(splitspeech[currentIndex].trim(), TextToSpeech.QUEUE_FLUSH, myHash);
}

// 别忘了在Activity/Fragment销毁时释放TTS
@Override
protected void onDestroy() {
    if (textToSpeech != null) {
        textToSpeech.stop();
        textToSpeech.shutdown();
    }
    super.onDestroy();
}

关键修改点解释

  • UtteranceProgressListener:监听每个词汇的播放完成事件,确保播放完一个再播下一个,这样我们能精准控制播放流程。
  • currentIndex变量:代替原来的ij,统一记录当前播放到的词汇索引,暂停时直接保存这个值,继续播放时从这里开始。
  • keepGoing标志位:在speakNextWord和监听器的onDone方法里都检查这个标志,一旦设置为false,就停止继续播放下一个词汇。
  • 暂停逻辑简化:暂停时直接调用textToSpeech.stop()停止当前播放,同时记录currentIndex,不需要再调用冗余的speakSpeech方法。

这样修改后,你就能实现正常的播放/暂停功能,暂停时循环会被中断,也能准确获取最后播放的词汇索引啦。

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

火山引擎 最新活动