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

Swift保存语音Utterance到文件时的缓冲错误问题求助

修复AVSpeechSynthesizer写入音频文件的缓冲错误(iOS 16/17兼容)

之前使用的语音合成写入文件方案,在iOS 16中存在缓冲错误但功能仍能运行,到iOS 17首个测试版中直接完全失效,核心是音频缓冲格式不匹配导致的帧计数问题,以下是问题详情和修复方案:

问题表现

  • iOS 16:运行时会打印约20次错误信息:

    2023-06-17 15:35:33.811838-0400 RecordSpeechFix[3899:1958883] [AXTTSCommon] TTSPlaybackEnqueueFullAudioQueueBuffer: error -66686 enqueueing buffer
    但完成回调能正常触发,文件可正常生成。

  • iOS 17(首个测试版):报错信息更明确:

    Input data proc returned inconsistent 512 packets for 2,048 bytes; at 2 bytes per packet, that is actually 1,024 packets
    功能完全失效,完成回调无法触发,文件生成失败。

问题原因

AVSpeechSynthesizer输出的音频缓冲默认是浮点格式(pcmFormatFloat32),但原代码在创建AVAudioFile时强制指定了commonFormat: .pcmFormatInt16,这种跨格式写入会导致帧长度和字节数的计算不匹配。iOS 16对这种不匹配的校验较宽松,仍能勉强运行;iOS 17收紧了校验逻辑,直接触发错误导致流程中断。

修复方案

移除强制格式转换的参数,让AVAudioFile使用缓冲的原生格式写入即可解决问题。修改saveSpeechUtteranceToFile函数中创建AVAudioFile的代码:

修复后的完整代码

import AVFoundation
import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundColor(.accentColor)
            Text("Record Speech")
        }
        .padding()
        .onTapGesture {
            saveSpeechUtteranceToFile(phrase: "This produces warnings.") {
                print("In iOS 16, this will print.  In iOS17, this will not.")
            }
        }
    }
    
    func documentsDirectory(fileName: String, ext: String) -> URL {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        let documentsDirectoryURL = paths[0]
    
        return documentsDirectoryURL.appendingPathComponent("\(fileName).\(ext)")
    }
    
    let synthesizer = AVSpeechSynthesizer()
    
    func saveSpeechUtteranceToFile(phrase: String, completionHandler: @escaping () -> ()) {
        
        let fileURL = documentsDirectory(fileName: "Test", ext: "caf")
        
        let utterance = AVSpeechUtterance(string: phrase)
        utterance.voice = AVSpeechSynthesisVoice(language: "en-US")
        utterance.rate = 0.50
        utterance.volume = 1.0
    
        var output: AVAudioFile?
        
        synthesizer.write(utterance) {(buffer: AVAudioBuffer) in
            guard let pcmBuffer = buffer as? AVAudioPCMBuffer else {
                fatalError("unknown buffer type: \(buffer)")
            }
            
            if pcmBuffer.frameLength == 0 {
                // Done
                completionHandler()
            } else {
                do{
                    if output == nil {
                        // 移除强制格式转换,使用缓冲原生格式写入
                        try output = AVAudioFile(
                            forWriting: fileURL,
                            settings: pcmBuffer.format.settings,
                            interleaved: false)
                    }
                    try output?.write(from: pcmBuffer)
                }catch {
                    print("Buffer error: \(error.localizedDescription)")
                }
            }
        }
    }
}

关键修复点

  1. 移除commonFormat: .pcmFormatInt16参数,避免强制格式转换导致的帧计数不匹配
  2. 修正文件扩展名的小问题(原代码中ext: ".caf"会生成Test..caf,改为ext: "caf"更合理)
  3. 优化错误打印,输出具体错误信息便于排查

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 13:22:52