Swift/Cocoa开发:如何将AVMutableComposition导出为wav或aiff音频文件
完整可运行实现代码
import AVFoundation func exportAudioToWav(composition: AVMutableComposition, outputURL: URL, completion: @escaping (Bool, Error?) -> Void) { // 提前删除同路径已存在的文件,避免写入失败 try? FileManager.default.removeItem(at: outputURL) // 初始化资源读取器 guard let assetReader = try? AVAssetReader(asset: composition) else { completion(false, NSError(domain: "AudioExport", code: -1, userInfo: [NSLocalizedDescriptionKey: "资源读取器初始化失败"])) return } // 读取合成对象中的音频轨道 guard let audioTrack = composition.tracks(withMediaType: .audio).first else { completion(false, NSError(domain: "AudioExport", code: -2, userInfo: [NSLocalizedDescriptionKey: "未找到音频轨道"])) return } // 配置读取器输出参数:直接输出WAV要求的LinearPCM格式 let readerOutputSettings: [String: Any] = [ AVFormatIDKey: kAudioFormatLinearPCM, AVLinearPCMIsBigEndianKey: false, AVLinearPCMIsFloatKey: false, AVLinearPCMBitDepthKey: 16, AVLinearPCMIsNonInterleaved: false, AVSampleRateKey: 44100.0, AVNumberOfChannelsKey: 2 ] let readerOutput = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: readerOutputSettings) readerOutput.alwaysCopiesSampleData = false if assetReader.canAdd(readerOutput) { assetReader.add(readerOutput) } // 初始化资源写入器,指定输出格式为wav guard let assetWriter = try? AVAssetWriter(outputURL: outputURL, fileType: .wav) else { completion(false, NSError(domain: "AudioExport", code: -3, userInfo: [NSLocalizedDescriptionKey: "资源写入器初始化失败"])) return } // 写入器输入参数和读取器输出参数保持一致 let writerInputSettings: [String: Any] = readerOutputSettings let writerInput = AVAssetWriterInput(mediaType: .audio, outputSettings: writerInputSettings) writerInput.expectsMediaDataInRealTime = false if assetWriter.canAdd(writerInput) { assetWriter.add(writerInput) } // 启动读写流程 assetWriter.startWriting() assetWriter.startSession(atSourceTime: .zero) guard assetReader.startReading() else { completion(false, assetReader.error) return } // 用自定义串行队列处理数据读写,避免阻塞主线程 let processQueue = DispatchQueue(label: "com.audio.export.queue") writerInput.requestMediaDataWhenReady(on: processQueue) { while writerInput.isReadyForMoreMediaData { // 从读取器拉取音频样本 guard let sampleBuffer = readerOutput.copyNextSampleBuffer() else { // 读取完成或出现错误 if assetReader.status == .completed { writerInput.markAsFinished() assetWriter.finishWriting { DispatchQueue.main.async { completion(assetWriter.status == .completed, assetWriter.error) } } } else { DispatchQueue.main.async { completion(false, assetReader.error) } } return } // 将样本写入输出文件 if !writerInput.append(sampleBuffer) { DispatchQueue.main.async { completion(false, assetWriter.error) } return } CMSampleBufferInvalidate(sampleBuffer) } } }
使用示例
// 你的音频合成对象,已完成轨道添加逻辑 let composition = AVMutableComposition() // 此处省略你添加音频轨道、编辑时间线的代码... // 生成输出文件路径,示例为存到沙盒Documents目录 let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] let outputURL = documentsDir.appendingPathComponent("exported_audio.wav") // 调用导出方法 exportAudioToWav(composition: composition, outputURL: outputURL) { success, error in if success { print("导出成功,文件路径:\(outputURL.path)") } else { print("导出失败:\(error?.localizedDescription ?? "未知错误")") } }
注意事项
- 如果需要调整音频参数(比如单声道、48k采样率、32位深度),直接修改
readerOutputSettings里的对应字段即可,只要符合LinearPCM格式规范都可以正常生成WAV文件 - 如果你的
AVMutableComposition包含多条音频轨道,给AVAssetReader添加对应数量的AVAssetReaderTrackOutput分别读取写入即可 - 导出前请确保合成对象的音频轨道、时间范围配置完整正确
内容的提问来源于stack exchange,提问作者de.
相关产品推荐
相关产品推荐

