iosswiftavaudiosessionavkit

IOS app crashes with "required condition is false: IsFormatSampleRateAndChannelCountValid(format)'" when the mic is selected


I am getting an application crash of my app when I am using mic in my case Microsoft Teams on the background and trying to record an audio inside of my app.

Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio', reason: 'required condition is false: IsFormatSampleRateAndChannelCountValid(format)'

Please refer to the code below:

func startRecording() {
        
        // Clear all previous session data and cancel task
        if recognitionTask != nil {
            recognitionTask?.cancel()
            recognitionTask = nil
        }

        // Create instance of audio session to record voice
        let audioSession = AVAudioSession.sharedInstance()
        do {
            try audioSession.setCategory(AVAudioSession.Category.record, mode: AVAudioSession.Mode.measurement, options: AVAudioSession.CategoryOptions.defaultToSpeaker)
            try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
        } catch {
            print("audioSession properties weren't set because of an error.")
        }
    
        self.recognitionRequest = SFSpeechAudioBufferRecognitionRequest()

        let inputNode = audioEngine.inputNode

        guard let recognitionRequest = recognitionRequest else {
            fatalError("Unable to create an SFSpeechAudioBufferRecognitionRequest object")
        }

        recognitionRequest.shouldReportPartialResults = true

        self.recognitionTask = speechRecognizer?.recognitionTask(with: recognitionRequest, resultHandler: { (result, error) in

            var isFinal = false

            if result != nil {

                self.textField.text = result?.bestTranscription.formattedString
                isFinal = (result?.isFinal)!
            }

            if error != nil || isFinal {

                self.audioEngine.stop()
                inputNode.removeTap(onBus: 0)

                self.recognitionRequest = nil
                self.recognitionTask = nil

                self.micButton.isEnabled = true
            }
        })
    
        let recordingFormat = inputNode.outputFormat(forBus: 0)

    
        inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer, when) in
            self.recognitionRequest?.append(buffer)
        }

        self.audioEngine.prepare()

        do {
            try self.audioEngine.start()
        } catch {
            print("audioEngine couldn't start because of an error.")
        }

        self.textField.text = ""
    }

I am pretty sure that the problem is somewhere here, but not sure how to fix it.

let recordingFormat = inputNode.outputFormat(forBus: 0)
        inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer, when) in
            self.recognitionRequest?.append(buffer)
        }

Solution

  • So the app was crashing because I didn't applied the correct microphone channel.

    1. Create a protocol on the top of your code after the imports to represent an error in the file where u have:

      let audioEngine = AVAudioEngine()
      
      protocol FeedbackViewDelegate : AnyObject {
          func showFeedbackError(title: String, message: String)
          func audioDidStart(forType type : FeedbackViewType)
      }
      
    2. In the beginning add the return of boolean in your function

      func startRecording() -> Bool {
      }
      
    3. Add this line of code in the sharedInstance catch part (it will prevent crash)

      let audioSession = AVAudioSession.sharedInstance()
      do {
          try audioSession.setCategory(AVAudioSession.Category.playAndRecord, mode: AVAudioSession.Mode.measurement, options: AVAudioSession.CategoryOptions.defaultToSpeaker)
          try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
      } catch {
          print("audioSession properties weren't set because of an error.")
          delegate?.showFeedbackError(title: "Sorry", message: "Mic is busy")
          return false
      }
      

      Return above will prevent code to be executed.

    4. Create an extension in the view controller

      extension codeFileName : FeedbackViewDelegate {
          func showFeedbackError(title: String, message: String) {
      
          }
      }
      

      (there is a millions of examples in the web) inside of the function you can create an alert and in "in" part use self