Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
woodymoo opened this issue Apr 5, 2024 · 3 comments
Labels

Comments

@woodymoo
Copy link

woodymoo commented Apr 5, 2024

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

@woodymoo woodymoo added the bug label Apr 5, 2024
@NickCulbertson
Copy link
Member

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.

@woodymoo
Copy link
Author

woodymoo commented Apr 7, 2024

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

@NickCulbertson
Copy link
Member

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants