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

为何在speechSynthesizer代理中调用audioSession.setActive(false)总是失败?

AVAudioSession setActive(false) 在语音合成完成后调用失败的原因与解决办法

核心原因分析

  1. 语音合成资源未完全释放:speechSynthesizer(_:didFinish:)回调触发时,AVSpeechSynthesizer的底层音频处理线程可能还没彻底结束,此时音频会话仍被系统标记为“在使用中”,直接调用setActive(false)会被拒绝。
  2. 错误处理缺失:初始化和激活会话时使用try?吞掉了所有错误,可能会话本身就没成功激活,后续的停用操作自然会失败。
  3. 停用选项未正确设置:默认调用setActive(false)时,系统不会通知其他音频应用恢复播放,部分场景下系统会阻止会话停用。

具体解决办法

  • 延迟执行停用操作:给底层资源留够释放时间,比如延迟0.5秒再调用停用方法。
  • 完善全链路错误处理:替换所有try?为do-catch,排查会话初始化、激活阶段的问题。
  • 添加停用选项:调用setActive(false, options: .notifyOthersOnDeactivation),明确通知其他应用恢复音频。
  • 保证激活/停用配对:避免多次调用setActive(true)但只调用一次setActive(false),可以用计数器跟踪激活次数。

修改后的示例代码

import Foundation
import AVFoundation

class Speaker: NSObject, AVSpeechSynthesizerDelegate, @unchecked Sendable {
    static let shared = Speaker()
    
    let audioSession = AVAudioSession.sharedInstance()
    let synthesizer = AVSpeechSynthesizer()
    private var activationCount = 0 // 跟踪会话激活次数
    
    override init() {
        super.init()
        synthesizer.delegate = self
        do {
            try audioSession.setCategory(.playback, options: [.duckOthers])
        } catch {
            print("会话类别设置失败:\(error.localizedDescription)")
        }
    }
    
    func speakChat(text: String) {
        let utterance = AVSpeechUtterance(string: text)
        utterance.rate = 0.5
        utterance.volume = 1.0
        
        activationCount += 1
        do {
            try audioSession.setActive(true)
        } catch {
            print("会话激活失败:\(error.localizedDescription)")
            activationCount -= 1
        }
        synthesizer.speak(utterance)
    }
    
    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
        activationCount -= 1
        // 只有当激活次数归0时才停用会话
        guard activationCount <= 0 else { return }
        
        // 延迟0.5秒执行,确保底层资源释放
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
            guard let self = self else { return }
            do {
                try self.audioSession.setActive(false, options: .notifyOthersOnDeactivation)
            } catch {
                print("会话停用失败:\(error.localizedDescription)")
            }
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 10:11:10