AudioKit / AudioKit

Audio synthesis, processing, & analysis platform for iOS, macOS and tvOS

Home Page:http://audiokit.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

I have following the cookbook Miniapp/Recorder sample to create an audioKit recorder but can not record...

woodymoo opened this issue · comments

macOS Version(s) Used to Build

macOS 13 Ventura

Xcode Version(s)

Xcode 14

Description

Xcode 15, IOS 17,
Development environment: Mac os 14.5.
I am trying to learn to use AudioKit and AudioKitUI, at first to make a small audio recorder and audio memo.
And I followed the Miniapp/Recorder to write a test app recorder, and save audio file. But the caf file is 4k and is empty!
Is there any issue with the AudioKit.NodeRecorder?
The following is part of the code:

struct RecorderData {
    var isRecording = false
//    var isPlaying = false
}

class RecorderConductor: ObservableObject, HasAudioEngine {
    let engine = AudioEngine()
    var recorder: NodeRecorder?
    let player = AudioPlayer()
    var silencer: Fader?
    var tappableNodeA : Fader
//    var tappableNodeB : Fader
    let mixer = Mixer()
    
    
    @Published var data = RecorderData() {
        didSet {
            if data.isRecording {
                do {
                    try recorder?.record()
                  
                } catch let err {
                    print(err)
                }
            } else {
                var file = recorder?.audioFile
                recorder?.stop()
                recorder?.closeFile(file: &file)
            }

//            if data.isPlaying {
//                if let file = recorder?.audioFile {
//                    try? player.load(file: file)
//                    player.play()
//                }
//            } else {
//                player.stop()
//            }
        }
    }

    init() {
#if os(iOS)
    do {
        
        AudioKit.Settings.bufferLength = .short
        try AVAudioSession.sharedInstance().setPreferredIOBufferDuration(AudioKit.Settings.bufferLength.duration)
        try AVAudioSession.sharedInstance().setCategory(.playAndRecord,
                                                        options: [.defaultToSpeaker, .mixWithOthers, .allowBluetoothA2DP])
        try AVAudioSession.sharedInstance().setActive(true)
    } catch let err {
        print(err)
    }
#endif
        
        print("RecorderConductor init")
        guard let input = engine.input else {
            fatalError()
        }

        
        do {
            recorder = try NodeRecorder(node: input, fileDirectoryURL: FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0], shouldCleanupRecordings: false)
        } catch let err {
            fatalError("\(err)")
        }

        tappableNodeA = Fader(input, gain: 10)
        silencer = Fader(tappableNodeA, gain: 0)
        engine.output = silencer
    }
    
    deinit {
        print("RecorderConductor deinit")
    }
}
struct AudioRecordView : View {
    @Environment(\.modelContext) private var modelContext
    @StateObject var recorderConductor = RecorderConductor()
    
    var body: some View {
        
        VStack {
           Text("Record Audio")
//            NodeRollingView(recorderConductor.tappableNodeA).clipped()
            NodeOutputView(recorderConductor.tappableNodeA).clipped()
            Spacer()
            
            HStack {
                Button(){
                    recorderConductor.data.isRecording.toggle()
                    if recorderConductor.data.isRecording {
                        print("Start recording")
                        let url = recorderConductor.recorder?.audioFile?.url
                        print("fileurl: \(url?.absoluteString ?? "no url") ")
                        let newItem = Item(url: url!, UUID: UUID())
                        
                        modelContext.insert(newItem)
                    } else {
                        print("Pause recording")
                    }
                } label: {
                    Image(
                        systemName: recorderConductor.data.isRecording ? "stop.circle.fill" : "record.circle.fill"   )
                        .resizable()
                       .aspectRatio(contentMode: .fit)
                       .frame(width: 50, height: 50)
                       .foregroundColor(Color.red)
                       
                }
                
                Button() {
                    print("Stop recording")
                } label: {
                    Image(systemName: "stop.circle.fill")
                        .resizable()
                       .aspectRatio(contentMode: .fit)
                       .frame(width: 50, height: 50)
                       .foregroundColor(Color.black)
                }
            }
            
        }
        .navigationTitle("Record Audio")
        .onAppear(){
            print("AudioRecordView appeared")
            recorderConductor.start()
        }
        .onDisappear(){
            print("AudioRecordView disappeared")
            recorderConductor.stop()
        }
    }
}

Crash Logs, Screenshots or Other Attachments (if applicable)

No response

If you remove this code it executes properly:

if recorderConductor.data.isRecording {
                        print("Start recording")
                        let url = recorderConductor.recorder?.audioFile?.url
                        print("fileurl: \(url?.absoluteString ?? "no url") ")
                        let newItem = Item(url: url!, UUID: UUID())
                        
                        modelContext.insert(newItem)
                    } else {
                        print("Pause recording")
                    }

It looks like accessing recorderConductor.recorder?.audioFile?.url while you are recording is causing an issue.

Thank you so much. The reason is that the NodeRecorder.audioFile explicitly close the file in the method.
The AudioKit.NodeRecorder has so limited features.
In NodeRecorder, I can not specify the AudioFormat for recording, can not specify the file extension...
it seems better to use AVaudioEngine, AVFundation directly, it is quite flexible

Because of the limitations you described, I think NodeRecorder is best used as an example starting point to create your own Recorder class. That's how I use it.