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

修改采样率时AVSampleRateKey引发崩溃,该如何解决?

AVAudioFile设置非44100采样率导致崩溃的问题分析与解决

在使用AVFoundation处理离线音频渲染时,通过AVSampleRateKey指定采样率时,若设置为44100以外的值(如示例中的48000),可能会引发崩溃。以下是问题复现的代码:

func manualAudioRender(effect: Effect) throws {
    prepareAudioEngine(forEffect: effect)
    
    audioPlayerNode.scheduleFile(avAudioFile, at: nil)
    try audioEngine.enableManualRenderingMode(.offline, format: avAudioFile.processingFormat, maximumFrameCount: maxNumberOfFrames)
    
    try audioEngine.start()
    audioPlayerNode.play()
    
    let outputFile: AVAudioFile
    let url = try PersistenceManager.shared.urlForEffect(named: effect.name)
    var recordSettings = avAudioFile.fileFormat.settings
    
    print("------>>>>. \(recordSettings)")
    
    recordSettings[AVSampleRateKey] = 48000.0
    
    print("------>>>>. \(recordSettings)")
  
//        var bitRate = audioBitrate
//        var sampleRate = 44100.0
//        var channels = 1
//
//        let settings: [String: AnyObject] = [
//                    AVFormatIDKey : NSNumber(value: Int32(kAudioFormatAppleLossless) as Int32),
//                    AVEncoderAudioQualityKey: AVAudioQuality.max.rawValue as AnyObject,
//                    AVEncoderBitRateKey: bitRate as AnyObject,
//                    AVNumberOfChannelsKey: channels as AnyObject,
//                    AVSampleRateKey: sampleRate as AnyObject
//                ]
    
    
    outputFile = try AVAudioFile(forWriting: url, settings: recordSettings)
    
    let buffer = AVAudioPCMBuffer(pcmFormat: audioEngine.manualRenderingFormat, frameCapacity: audioEngine.manualRenderingMaximumFrameCount)!
    
    // 根据效果速率调整文件长度
    let outputFileLength = Int64(Double(avAudioFile.length) / effect.rate)
    
    while audioEngine.manualRenderingSampleTime < outputFileLength {
        let framesToRender = min(buffer.frameCapacity, AVAudioFrameCount(outputFileLength - audioEngine.manualRenderingSampleTime))
        let status = try audioEngine.renderOffline(framesToRender, to: buffer)
        switch status {
        case .success:
            try outputFile.write(from: buffer)
        case .error:
            print("离线渲染音频出错")
            return
        default:
            break
        }
    }
    
    audioPlayerNode.stop()
    audioEngine.stop()
    audioEngine.disableManualRenderingMode()
}

问题核心原因

  • 格式不匹配:音频引擎手动渲染使用的是输入文件的processingFormat,但输出文件强制修改了采样率,导致渲染生成的PCM buffer格式与输出文件要求的格式不兼容,写入时触发崩溃。
  • 设置复用冲突:直接修改输入文件的fileFormat.settings并替换采样率,可能保留了原格式中与新采样率不兼容的参数(比如特定编码格式对采样率的限制)。

解决方法

1. 统一渲染与输出格式

创建与目标采样率匹配的音频格式,将其作为手动渲染模式的格式参数,确保渲染后的buffer格式与输出文件一致。

2. 显式构建兼容的输出设置

避免复用输入文件的设置,重新构建完整的输出设置字典,确保所有参数与目标采样率兼容。

修改后的完整代码如下:

func manualAudioRender(effect: Effect) throws {
    prepareAudioEngine(forEffect: effect)
    
    audioPlayerNode.scheduleFile(avAudioFile, at: nil)
    
    // 定义目标采样率
    let targetSampleRate = 48000.0
    // 创建与目标采样率匹配的音频格式
    guard let targetFormat = AVAudioFormat(
        commonFormat: avAudioFile.processingFormat.commonFormat,
        sampleRate: targetSampleRate,
        channels: avAudioFile.processingFormat.channelCount,
        interleaved: avAudioFile.processingFormat.isInterleaved
    ) else {
        throw NSError(domain: "AudioProcessingError", code: -1, userInfo: [NSLocalizedDescriptionKey: "无法创建符合要求的目标音频格式"])
    }
    
    // 使用目标格式启用离线渲染模式
    try audioEngine.enableManualRenderingMode(.offline, format: targetFormat, maximumFrameCount: maxNumberOfFrames)
    
    try audioEngine.start()
    audioPlayerNode.play()
    
    let url = try PersistenceManager.shared.urlForEffect(named: effect.name)
    // 显式构建输出文件设置,确保参数兼容
    let outputSettings: [String: Any] = [
        AVFormatIDKey: Int32(kAudioFormatAppleLossless),
        AVSampleRateKey: targetSampleRate,
        AVNumberOfChannelsKey: targetFormat.channelCount,
        AVEncoderAudioQualityKey: AVAudioQuality.max.rawValue
    ]
    
    let outputFile = try AVAudioFile(forWriting: url, settings: outputSettings)
    
    let buffer = AVAudioPCMBuffer(pcmFormat: targetFormat, frameCapacity: audioEngine.manualRenderingMaximumFrameCount)!
    
    // 根据效果速率调整输出文件长度
    let outputFileLength = Int64(Double(avAudioFile.length) / effect.rate)
    
    while audioEngine.manualRenderingSampleTime < outputFileLength {
        let framesToRender = min(buffer.frameCapacity, AVAudioFrameCount(outputFileLength - audioEngine.manualRenderingSampleTime))
        let status = try audioEngine.renderOffline(framesToRender, to: buffer)
        switch status {
        case .success:
            try outputFile.write(from: buffer)
        case .error:
            print("离线渲染音频出错")
            return
        default:
            break
        }
    }
    
    audioPlayerNode.stop()
    audioEngine.stop()
    audioEngine.disableManualRenderingMode()
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 09:41:03