iOS audio conversion from AVMutableComposition to AVAudioPlayer

818 views Asked by At

I been searching for a solution for this but so far I couldn't find it.

Right now I'm creating an audio file with AVMutableComposition and writing it to disk with AVAssetExportSession so I can load it with AVAudioPlayer and play it at a certain time. The fact that writing it to disk takes to much time is slowing things down and in some cases make my app very slow.

The creation part of the audio takes very little time, so I was wondering if insteed of exporting the file to disk I could just export it like NSData or something so I can use it in AVAudioPlayer without writing/reading from disk


code let soundPath = "temp" let filepath = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(soundPath, ofType: "wav")!)

    var songAsset = AVURLAsset(URL: filepath, options: nil)

    var track = composition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())
    var source: AnyObject = songAsset.tracksWithMediaType(AVMediaTypeAudio)[0]

    var startTime = CMTimeMakeWithSeconds(0, 44100)
    var time_insert = CMTimeMakeWithSeconds(atTime, 44100)
    var trackDuration = songAsset.duration
    var longestTime = CMTimeMake(882000, 44100)
    var timeRange = CMTimeRangeMake(startTime, longestTime)

    var trackMix = AVMutableAudioMixInputParameters(track: track)
    trackMix.setVolume(volume, atTime: startTime)
    audioMixParams.insert(trackMix, atIndex: audioMixParams.count)

    track.insertTimeRange(timeRange, ofTrack: source as AVAssetTrack, atTime: time_insert, error: nil)

This happens several times in a loop generating a whole track

Finally I use

    var exporter = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetAppleM4A)
    exporter.audioMix = audioMix
    exporter.outputFileType = "com.apple.m4a-audio"
    exporter.outputURL = NSURL(fileURLWithPath: fileName)

    exporter.exportAsynchronouslyWithCompletionHandler({
        switch exporter.status{
        case  AVAssetExportSessionStatus.Failed:
            println("failed \(exporter.error)")
        case AVAssetExportSessionStatus.Cancelled:
            println("cancelled \(exporter.error)")
        default:
            println("complete")
            callback()
        }
    })

to export the file.

and after that it calls a callback that loads the sound and loops it in a time.

The fact I don't loop with the normal loop property is that each file might not have the same length as loopDuration

    callback: {

        self.fillSoundArray()
        self.fillSoundArray()
        self.fillSoundArray()
    }


private func fillSoundArray() {
    var currentloop = self.current_loop++

    let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString
    var fileName = documentsPath + "/temp.mp4"

    var tempAudio = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: fileName), error: nil)
    if (self.start_time == 0) {
        self.start_time = tempAudio.deviceCurrentTime
    }

    queue.insert(tempAudio, atIndex: queue.count)
    tempAudio.prepareToPlay()
    tempAudio.delegate = self
    tempAudio.playAtTime(start_time + currentloop*self.loopDuration)
}
1

There are 1 answers

2
DimaC On

Use AVPlayerItem & AVPlayer to make a quick preview of your audio composition

AVPlayerItem*  previewPlayerItem = [[AVPlayerItem alloc] initWithAsset:mutableCompositionMain 
                                          automaticallyLoadedAssetKeys:@[@"tracks",@"duration"]];
previewPlayerItem.videoComposition = mutableVideoComposition;
AVPlayer* previewPlayer = [[AVPlayer alloc] initWithPlayerItem:previewPlayerItem ];
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(dismisPreviewPlayer:)
                                             name:AVPlayerItemDidPlayToEndTimeNotification
                                           object:[previewPlayer currentItem]];