Search code examples
iosswiftaudio-recording

append or concatenate audio files in swift


Hi I want to append voice files.

I'm recording voice with AVAudioRecorder, but to play the recording I need to call "stop", but after playing it I want to continue record. Like the native iOS Voice memo app.

Should I use AVMutableCompositionTrack and how do I do that in swift? Thanks!


Solution

  • If you are looking to simply pause your recording and continue it later you can use AVAudioRecorder's pause() function rather than stop() and it will continue the recording when you use play() again.

    However, if you are looking to actually concatenate audio files, you can do it like this:

    func concatenateFiles(audioFiles: [NSURL], completion: (concatenatedFile: NSURL?) -> ()) {
        guard audioFiles.count > 0 else {
            completion(concatenatedFile: nil)
            return
        }
    
        if audioFiles.count == 1 {
            completion(concatenatedFile: audioFiles.first)
            return
        }
    
        // Concatenate audio files into one file
        var nextClipStartTime = kCMTimeZero
        let composition = AVMutableComposition()
        let track = composition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid)
    
        // Add each track
        for recording in audioFiles {
            let asset = AVURLAsset(URL: NSURL(fileURLWithPath: recording.path!), options: nil)
            if let assetTrack = asset.tracksWithMediaType(AVMediaTypeAudio).first {
                let timeRange = CMTimeRange(start: kCMTimeZero, duration: asset.duration)
                do {
                    try track.insertTimeRange(timeRange, ofTrack: assetTrack, atTime: nextClipStartTime)
                    nextClipStartTime = CMTimeAdd(nextClipStartTime, timeRange.duration)
                } catch {
                    print("Error concatenating file - \(error)")
                    completion(concatenatedFile: nil)
                    return
                }
            }
        }
    
        // Export the new file
        if let exportSession = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetPassthrough) {
            let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
            let documents = NSURL(string: paths.first!)
    
            if let fileURL = documents?.URLByAppendingPathComponent("file_name.caf") {
                // Remove existing file
                do {
                    try NSFileManager.defaultManager().removeItemAtPath(fileURL.path!)
                    print("Removed \(fileURL)")
                } catch {
                    print("Could not remove file - \(error)")
                }
    
                // Configure export session output
                exportSession.outputURL = NSURL.fileURLWithPath(fileURL.path!)
                exportSession.outputFileType = AVFileTypeCoreAudioFormat
    
                // Perform the export
                exportSession.exportAsynchronouslyWithCompletionHandler() { handler -> Void in
                    if exportSession.status == .Completed {
                        print("Export complete")
                        dispatch_async(dispatch_get_main_queue(), {
                            completion(file: fileURL)
                        })
                        return
                    } else if exportSession.status == .Failed {
                        print("Export failed - \(exportSession.error)")
                    }
    
                    completion(concatenatedFile: nil)
                    return
                }
            }
        }
    }