iOS 将 2 个或多个音频文件合并(混合)为 wav 格式

Posted

技术标签:

【中文标题】iOS 将 2 个或多个音频文件合并(混合)为 wav 格式【英文标题】:iOS merge (mix) 2 or more audio files to wav format 【发布时间】:2016-06-07 13:12:03 【问题描述】:

我在下面创建了合并方法(加入、联合,我不确定哪个是正确的词,我想从 2 个或更多音频中制作 1 个音频,而不是一个接一个,而是一次播放每个音频) .作为输入,我有多个 .wav 格式的音频文件,我希望输出 1 个 .wav 格式。

  func merge(audioUrls: [NSURL], resultName : String = "result") 

    let resultNameWithExtension = resultName + ".wav"

    //Create AVMutableComposition Object.This object will hold our multiple AVMutableCompositionTrack.
    let composition = AVMutableComposition()


    //create new file to receive data
    //let documentDirectoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
    let outputFilePath = NSTemporaryDirectory().stringByAppendingPathComponent(resultNameWithExtension)
    let fileDestinationUrl = NSURL(fileURLWithPath: outputFilePath)
    print(fileDestinationUrl)

    StorageManager.sharedInstance.deleteFileAtPath(NSTemporaryDirectory().stringByAppendingPathComponent(resultNameWithExtension))

    var avAssets: [AVURLAsset] = []
    var assetTracks: [AVAssetTrack] = []
    var timeRanges: [CMTimeRange] = []

    for audioUrl in audioUrls 
      let compositionAudioTrack:AVMutableCompositionTrack = composition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID:kCMPersistentTrackID_Invalid)

      let avAsset = AVURLAsset(URL: audioUrl, options: nil)
      avAssets.append(avAsset)

      let assetTrack = avAsset.tracksWithMediaType(AVMediaTypeAudio)[0]
      assetTracks.append(assetTrack)

      let duration = assetTrack.timeRange.duration
      let timeRange = CMTimeRangeMake(kCMTimeZero, duration)
      timeRanges.append(timeRange)

      do 
        try compositionAudioTrack.insertTimeRange(timeRange, ofTrack: assetTrack, atTime: kCMTimeZero)
       catch let error as NSError 
        print("compositionAudioTrack insert error: \(error)")
      
    

    let assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetPassthrough)!
    assetExport.outputFileType = AVFileTypeWAVE
    assetExport.outputURL = fileDestinationUrl
    assetExport.exportAsynchronouslyWithCompletionHandler(
      self.delegate?.assetExportSessionDidFinishExport(assetExport, outputFilePath: outputFilePath)
    )
  

我的问题是它不起作用,我不知道为什么。我得到的错误:

错误域=AVFoundationErrorDomain 代码=-11838 “操作已停止” UserInfo=NSLocalizedDescription=操作已停止, NSLocalizedFailureReason=此操作不支持 媒体。)

当我将预设和输出类型更改为 .m4a 时,它可以工作,但我需要 .wav。当我有相同格式的输入时,它应该与 .wav 一起使用,对吗?感谢您的帮助

【问题讨论】:

Mixing 或 summing 是音乐世界中合并音频的正确术语。 感谢您的信息,所以我需要混合我的音频文件。 我过去做过很多测试,但我从来没有能够让 AVAssetExportSession 导出 M4A 文件以外的任何东西。您可能需要为您的任务使用与 AVAssetExportSession 不同的 API。 AVMutableAudioMix 有明确的预设,除了 M4A 文件之外不能导出任何东西。我试图回答这个问题,但它没有奏效。并且在使用 AVAssetExportPresetPassthrough 时,它会忽略 audioMix。我努力让事情成功,但没有成功。 @EricD 我的任务有不同的 API 吗?可以帮助我的另一个可能的课程是什么?谢谢 【参考方案1】:

如果您想将一个音频文件混合或重叠到另一个音频文件,那么您应该编写此代码,但您只能生成 .m4a 文件而不是 .wav 文件。我使用 .mp3 文件作为输入文件。

在 swift3 中:

例如:

func playmerge(audio1: NSURL, audio2:  NSURL)

    let composition = AVMutableComposition()
    let compositionAudioTrack1:AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())
    let compositionAudioTrack2:AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())

    let documentDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! as NSURL
    self.fileDestinationUrl = documentDirectoryURL.appendingPathComponent("resultmerge.m4a")! as URL

    let filemanager = FileManager.default
    if (!filemanager.fileExists(atPath: self.fileDestinationUrl.path))
    
        do
        
            try filemanager.removeItem(at: self.fileDestinationUrl)
        
        catch let error as NSError
        
            NSLog("Error: \(error)")
        

        if (theError == nil)
        
            print("The music files has been Removed.")
        
        else
        
            print("Error")
        
    
    else
    
        do
        
            try filemanager.removeItem(at: self.fileDestinationUrl)
        
        catch let error as NSError
        
            NSLog("Error: \(error)")
        

        if (theError == nil)
        
            print("The music files has been Removed.")
        
        else
        
            print("Error")
        
    

    let url1 = audio1
    let url2 = audio2

    let avAsset1 = AVURLAsset(url: url1 as URL, options: nil)
    let avAsset2 = AVURLAsset(url: url2 as URL, options: nil)

    var tracks1 = avAsset1.tracks(withMediaType: AVMediaTypeAudio)
    var tracks2 = avAsset2.tracks(withMediaType: AVMediaTypeAudio)

    let assetTrack1:AVAssetTrack = tracks1[0]
    let assetTrack2:AVAssetTrack = tracks2[0]

    let duration1: CMTime = assetTrack1.timeRange.duration
    let duration2: CMTime = assetTrack2.timeRange.duration

    let timeRange1 = CMTimeRangeMake(kCMTimeZero, duration1)
    let timeRange2 = CMTimeRangeMake(kCMTimeZero, duration2)
    do
    
        try compositionAudioTrack1.insertTimeRange(timeRange1, of: assetTrack1, at: kCMTimeZero)
        try compositionAudioTrack2.insertTimeRange(timeRange2, of: assetTrack2, at: kCMTimeZero)
    
    catch
    
        print(error)
    

    let assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetAppleM4A)
    assetExport?.outputFileType = AVFileTypeAppleM4A
    assetExport?.outputURL = fileDestinationUrl
    assetExport?.exportAsynchronously(completionHandler:
        
        switch assetExport!.status
        
        case AVAssetExportSessionStatus.failed:
            print("failed \(assetExport?.error)")
        case AVAssetExportSessionStatus.cancelled:
            print("cancelled \(assetExport?.error)")
        case AVAssetExportSessionStatus.unknown:
            print("unknown\(assetExport?.error)")
        case AVAssetExportSessionStatus.waiting:
            print("waiting\(assetExport?.error)")
        case AVAssetExportSessionStatus.exporting:
            print("exporting\(assetExport?.error)")
        default:
            print("complete")
        

        do
        
            self.player = try AVAudioPlayer(contentsOf: self.fileDestinationUrl)
            self.player?.numberOfLoops = 0
            self.player?.prepareToPlay()
            self.player?.volume = 1.0
            self.player?.play()
            self.player?.delegate=self
        
        catch let error as NSError
        
            print(error)
        
    )

【讨论】:

只粘贴代码是不够的,应该解释清楚。 感谢您的帮助!【参考方案2】:

请参阅this question,这似乎是自 ios 7 以来的一个突出错误。不幸的是,DTS 提出的提交错误的建议目前似乎仍然适用。

您可以尝试使用 AVAssetWriter 导出,按照此处的代码行:Converting CAF to WAV。

【讨论】:

这里也有有用的代码:developer.apple.com/library/ios/samplecode/ReaderWriter/…

以上是关于iOS 将 2 个或多个音频文件合并(混合)为 wav 格式的主要内容,如果未能解决你的问题,请参考以下文章

iOS - 混合两个音频文件

iOS - 将两个视频合并为一个带音频的视频文件

如何将多个不同频率的音调混合为一个

C++ 音频混合

在 iOS 上使用音频单元混合多个信号

在 iOS 上混合和均衡多个压缩音频流