【问题标题】:Swift/Cocoa: How do I export an AVMutableComposition as wav or aiff audio fileSwift/Cocoa:如何将 AVMutableComposition 导出为 wav 或 aiff 音频文件
【发布时间】:2021-10-11 19:40:40
【问题描述】:

我有一个 AVMutableComposition 只包含我想导出到 .wav 音频文件的音频。

我发现的最简单的音频导出解决方案是使用AVAssetExportSession,就像在这个简化的示例中一样:

let composition = AVMutableComposition()
// add tracks...

let exportSession = AVAssetExportSession(asset: composition,
                                         presetName: AVAssetExportPresetAppleM4A)!
exportSession.outputFileType = .m4a
exportSession.outputURL = someOutUrl
exportSession.exportAsynchronously {
    // done
}

但它只适用于 .m4a

这个post 提到要导出到其他格式,必须使用AVAssetReaderAVAssetWriter,但遗憾的是它没有详细说明。

我曾尝试实现它,但被卡在了这个过程中。
这是我到目前为止所拥有的(再次简化):

let composition = AVMutableComposition()
let outputSettings: [String : Any] = [
    AVFormatIDKey: kAudioFormatLinearPCM,
    AVLinearPCMIsBigEndianKey: false,
    AVLinearPCMIsFloatKey: false,
    AVLinearPCMBitDepthKey: 32,
    AVLinearPCMIsNonInterleaved: false,
    AVSampleRateKey: 44100.0,
    AVChannelLayoutKey: NSData(),
]

let assetWriter = try! AVAssetWriter(outputURL: someOutUrl, fileType: .wav)
let input = AVAssetWriterInput(mediaType: .audio, outputSettings: outputSettings)
assetWriter.add(input)
assetWriter.startWriting()
assetWriter.startSession(atSourceTime: CMTime.zero)
input.requestMediaDataWhenReady(on: .main) {

    // as I understand, I need to bring in data from my
    // AVMutableComposition here...

    let sampleBuffer: CMSampleBuffer = ???
    input.append(sampleBuffer)
}

assetWriter.finishWriting {
    // done
}

归结为我的问题:

您能否提供一个将音频从 AVMutableComposition 导出到 wav 文件的工作示例?

【问题讨论】:

    标签: swift avfoundation core-audio avassetwriter


    【解决方案1】:

    经过更多研究,我想出了以下解决方案。
    缺少的部分是 AVAssetReader 的使用。

    (简化代码)

    // composition
    let composition = AVMutableComposition()
    // add stuff to composition
    
    // reader
    guard let assetReader = try? AVAssetReader(asset: composition) else { return }
    assetReader.timeRange = CMTimeRange(start: .zero, duration: CMTime(value: composition.duration.value, timescale: composition.duration.timescale))
    let assetReaderAudioMixOutput = AVAssetReaderAudioMixOutput(audioTracks: composition.tracks(withMediaType: .audio), audioSettings: nil)
    assetReader.add(assetReaderAudioMixOutput)
    guard assetReader.startReading() else { return }
    
    // writer
    let outputSettings: [String : Any] = [
        AVFormatIDKey: kAudioFormatLinearPCM,
        AVLinearPCMIsBigEndianKey: false,
        AVLinearPCMIsFloatKey: false,
        AVLinearPCMBitDepthKey: 32,
        AVLinearPCMIsNonInterleaved: false,
        AVSampleRateKey: 44100.0,
        AVChannelLayoutKey: NSData(),
    ]
    guard let assetWriter = try? AVAssetWriter(outputURL: someOutUrl, fileType: .wav) else { return }
    let writerInput = AVAssetWriterInput(mediaType: .audio, outputSettings: outputSettings)
    assetWriter.add(writerInput)
    guard assetWriter.startWriting() else { return }
    assetWriter.startSession(atSourceTime: CMTime.zero)
    
    let queue = DispatchQueue(label: "my.queue.id")
    input.requestMediaDataWhenReady(on: queue) {
    
        // capture assetReader in my block to prevent it being released
        let readerOutput = assetReader.outputs.first!
    
        while writerInput.isReadyForMoreMediaData {
            if let nextSampleBuffer = readerOutput.copyNextSampleBuffer() {
                writerInput.append(nextSampleBuffer)
            } else {
                writerInput.markAsFinished()
                assetWriter.endSession(atSourceTime: composition.duration)
                assetWriter.finishWriting() {
                    DispatchQueue.main.async {
    
                        // done, call my completion
                    }
                }
                break;
            }
        }
    }
    
    
    

    【讨论】:

      【解决方案2】:

      正如您所说,您正在成功创建 .m4a 为什么不将 .m4a 文件转换为 .wav 文件,只需输入几行代码

      我只是将文件的扩展名更改为 .wav 并删除了 .m4a 文件,它就可以工作了。

      func getDirectory() -> URL {
          let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
          let documentDirectory = path[0]
          return documentDirectory
      }
      
      let date = Date().timeIntervalSince1970
      
      fileName = getDirectory().appendingPathComponent("\(date).m4a")
      wavFileName = getDirectory().appendingPathComponent("\(date).wav")
      
      try! FileManager.default.copyItem(at: fileName, to: wavFileName)
      try! FileManager.default.removeItem(at: fileName)
      

      我什至播放了 .wav 文件,它运行良好。

      audioPlayer = try! AVAudioPlayer(contentsOf: wavFileName)
      audioPlayer.play()
      

      查看上面的示例,此扩展替换是否会导致您的应用中的任何负载或时间

      【讨论】:

      • 所以你建议只是重命名文件?它仍然是一个 .m4a,只是名称不同。
      • 不,伙计,它不会是 .m4a 文件,我们正在更改其扩展名而不是名称
      • 如果您发现此答案有用,您可以将其标记为正确
      猜你喜欢
      • 1970-01-01
      • 2020-06-14
      • 2014-02-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-05
      • 2019-12-31
      • 2023-03-23
      相关资源
      最近更新 更多