Search code examples
audiokit

Can I get AudioKit appendAsynchronously to create a file with name other than '.caf', and to output in .mp4 format?


I have the following AudioKit usage scenario under iOS:

  1. I record a sound from the microphone.
  2. I save the sound asynchronously to the tmp directory as an .mp4 file.
  3. I record a second sound from the microphone.
  4. I attempt to (a) retrieve the previously saved .mp4 file, and (b) asynchronously append to it the second microphone sound.

My problem is that while this process does succeed, the appended file is saved with the filename '.caf' (literally just that, with nothing before the extension).

Ideally, I would like some way of forcing the appended file to have a more manageable name. It would also be nice if the appended file could be in an .mp4 format.

The following code suffices to reproduce the issue:

import SwiftUI import AudioKit

struct ContentView: View {

@State private var mic: AKMicrophone!
@State private var micBooster: AKBooster!
@State private var micRecorder: AKNodeRecorder! 

var body: some View {
    Button(action: {
        self.recordFirstSound()
    }) {
        Text("Start")
    }
}


func recordFirstSound() {
    
    AKAudioFile.cleanTempDirectory()
    AKSettings.audioInputEnabled = true
    AKSettings.defaultToSpeaker = true
    do {
        try AKSettings.setSession(category: .multiRoute)
    } catch {
        print("Failed to set session category to .playAndRecord");
    }
    AKSettings.bufferLength = .medium
    mic = AKMicrophone()
    micBooster = AKBooster()
    mic >>> micBooster
    micBooster.gain = 0.0
    
    do {
        micRecorder = try AKNodeRecorder(node: mic)
    } catch {
        print("Failed to initialise micRecorder")
    }
    
    AudioKit.output = micBooster
    do {
        try AudioKit.start()
    } catch {
        print("Failed to start AudioKit")
    }
    
    do {
        try micRecorder.record()
    } catch {
        print("Failed to start micRecorder")
    }
    
    DispatchQueue.main.asyncAfter(deadline: .now() + 5 , execute: {
        self.writeRecordingToTmpDir()
    })
}

func writeRecordingToTmpDir() {
    
    micRecorder.stop()
    if let recorderAudioFile = micRecorder.audioFile {
        recorderAudioFile.exportAsynchronously(name: "recording",
                                               baseDir: .temp,
                                               exportFormat: .mp4, callback: callbackAfterInitialExport)
    } else {
        print("Problem accessing micRecorder audioFile")
    }
    
    DispatchQueue.main.asyncAfter(deadline: .now() + 5 , execute: {
        self.recordSecondSound()
    })
}

func recordSecondSound() {
    do {
        try micRecorder.reset()
    } catch {
        print("Failed to reset micRecorder")
    }

    do {
        try micRecorder.record()
    } catch {
        print("Failed to re-start micRecorder")
    }
    
    DispatchQueue.main.asyncAfter(deadline: .now() + 5 , execute: {
        self.appendSecondSoundFileToFirstAsynchronously()
    })
}


func appendSecondSoundFileToFirstAsynchronously() {
    micRecorder.stop()
    do {
        let existingMp4File = try AKAudioFile(readFileName: "recording.mp4", baseDir: .temp)
        if let micRecorderAudioFile = micRecorder.audioFile {
            existingMp4File.appendAsynchronously(file: micRecorderAudioFile, completionHandler: callbackAfterAsynchronousAppend)
        }
    } catch {
        print("Failed to read or append recording.mp4")
    }
}


func callbackAfterInitialExport(processedFile: AKAudioFile?, error: NSError?) {
    if let file = processedFile {
        print("Asynchronous export of \(file.fileNamePlusExtension) succeeded")
        print("Exported file duration: \(file.duration) seconds")
    } else {
        print("Asynchronous export failed")
    }
}

func callbackAfterAsynchronousAppend(processedFile: AKAudioFile?, error: NSError?) {
    
    if let file = processedFile {
        print("Asynchronous append succeeded. New file is \(file.fileNamePlusExtension)")
        print("Duration of new file: \(file.duration) seconds")
    } else {
        print("Asynchronous append failed")
    }
}

}


Solution

  • Wasn't populating appropriate parameters for appendAsynchronously call. Call should be:

    if let micRecorderAudioFile = micRecorder.audioFile { existingMp4File.appendAsynchronously(file: micRecorderAudioFile, baseDir: .temp, name: "recording", completionHandler: callbackAfterAsynchronousAppend) }