apply an effect -> save to wav file" chain using AudioKit in Swift. Below is the code I'm trying to" /> apply an effect -> save to wav file" chain using AudioKit in Swift. Below is the code I'm trying to" /> apply an effect -> save to wav file" chain using AudioKit in Swift. Below is the code I'm trying to"/>

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

89 Views Asked by At

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.")
        }
    }
}
2

There are 2 best solutions below

1
mahal tertin On BEST ANSWER

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

0
Xcoder On

The NodeRecorder has been connected to the input, however to start recording you have to call record()

do {
    try recorder?.record()
}
catch let error {
    print(error)
}

Stop the recording by calling recorder?.stop(). Also, noticed that you are feeding the microphone input to the NodeRecorder. If you need to write the pitch shifted samples, you have to feed the pitchShifter to the NodeRecorder.

recorder = try NodeRecorder(node: pitchShifter, fileDirectoryURL: outputFileURL)