3

I've created method below for merge (join, union, I am not sure which is right word for this, I want to make 1 audio from 2 or more and not one after each other but to play each at once). As inputs I have multiple audio files in .wav format and I want 1 .wav format at output.

  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)
    })
  }

My problem is that it's not working and I don't know why. The error I get:

Error Domain=AVFoundationErrorDomain Code=-11838 "Operation Stopped" UserInfo={NSLocalizedDescription=Operation Stopped, NSLocalizedFailureReason=The operation is not supported for this media.})

When I change preset and output type to .m4a it's working but I need .wav. It should be working with .wav when I have inputs in same format right? Thanks for any help

Libor Zapletal
  • 13,752
  • 20
  • 95
  • 182
  • Mixing or summing is the correct term for merging audio in the music world. – Simon Bosley Jun 07 '16 at 14:25
  • Thanks for info, so I need to mix my audio files. – Libor Zapletal Jun 07 '16 at 15:13
  • 1
    I've made many tests in the past and I've never been able to make AVAssetExportSession export anything else than M4A files. You may need to use a different API than AVAssetExportSession for your task. – Eric Aya Jun 11 '16 at 14:00
  • AVMutableAudioMix has definite presets and won't be able to export anything other than M4A files. I attempted to answer this question but it just didn't work. And when using, AVAssetExportPresetPassthrough, it ignores audioMix. I tried hard to make things work by didn't work. – Andre Yonadam Jun 13 '16 at 00:23
  • @EricD And is there different API for my task? What's another possible class that could help me? Thanks – Libor Zapletal Jun 13 '16 at 06:49
  • @LiborZapletal, did you ever find a solution to this question? I need to concatenate audio files and keep the original quality for processing. It cannot be compressed. – grehce Sep 30 '19 at 17:56

2 Answers2

5

If you want to mix or overlap one audio file to another audio file then you should write this code,but you can only generate .m4a file not .wav file. I am use .mp3 files as input files.

In swift3:

For Example:

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)
        }
    })
}
Pragnesh Vitthani
  • 2,532
  • 20
  • 28
0

Refer to this question and it appears this has been an outstanding bug since iOS 7. The advice there from DTS to file a bug appears to still be currently applicable, unfortunately.

What you could try instead is exporting with AVAssetWriter, along the lines of the code here: Converting CAF to WAV.

Community
  • 1
  • 1
Alex Curylo
  • 4,744
  • 1
  • 27
  • 37
  • Useful code here too: https://developer.apple.com/library/ios/samplecode/ReaderWriter/Listings/Swift_AVReaderWriter_CyanifyOperation_swift.html – matt Jun 16 '16 at 21:02