swiftaudiokit

Swift - Can't figure out a simple "Mic->Effect->File" chain in AudioKit


I want to do a simple "record user's microphone -> apply an effect -> save to wav file" chain using AudioKit in Swift.

Below is the code I'm trying to use. What I get instead is that I hear the output through the headphones (with pitch effect applied), but output file is empty. What am I doing wrong?

import AudioKit
import AVFoundation
import SoundpipeAudioKit

class AudioRecorder: ObservableObject {
  
    let engine = AudioEngine()
    var recoder: NodeRecorder?

    private(set) lazy var outputFileURL: URL = {
        let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        return documentsPath.appendingPathComponent("recording.wav")
    }()

    func startRecording() {
        do {
            try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: [.defaultToSpeaker])
            try AVAudioSession.sharedInstance().setActive(true)
        } catch {
            print("Failed to set up recording session.")
            return
        }

        guard let input = engine.input else {
            print("No audio input found")
            return
        }

        do {
            recorder = try NodeRecorder(node: input, fileDirectoryURL: outputFileURL)
        } catch let err {
            fatalError("\(err)")
        }
        let pitchShifter = PitchShifter(input)
        engine.output = pitchShifter

        do {
            try engine.start()
        } catch {
            print("Unable to start audio engine: \(error.localizedDescription)")
            return
        }
    }

    func stopRecording() {
        engine.stop()
        do {
            try AVAudioSession.sharedInstance().setActive(false)
        } catch {
            print("Failed to stop recording session.")
        }
    }
}

Solution

  • Try changing following to make it work:

    1. Line var recoder: NodeRecorder? Change to var recorder: NodeRecorder? // missing r

    2. Line return documentsPath.appendingPathComponent("recording.wav") Change to return documentsPath // NodeRecorder wants a directory, not a file in fileDirectoryURL

    3. Add the pitchShifter to the audiograph before adding the recorder, as you want to record the shifted audio.

    4. Record and save to a .waf in two different steps. NodeRecorder records to a temporary .caf which you then can move or encode and send.

    5. NodeRecorder has some built in file management, beware of shouldCleanupRecordings: as it's default to true

    --

    let pitchShifter = PitchShifter(input)
    
    do {
        recorder = try NodeRecorder(node: pitchShifter, fileDirectoryURL: outputFileURL, shouldCleanupRecordings:false)
    } catch let err {
        fatalError("\(err)")
    }
    engine.output = pitchShifter
    

    Furthermore check out the Cookbook of AudioKit, this example will help: https://github.com/AudioKit/Cookbook/blob/main/Cookbook/CookbookCommon/Sources/CookbookCommon/Recipes/MiniApps/Recorder.swift