voice: [iOS] Crash on com.apple.coreaudio.avfaudio: required condition is false: IsFormatSampleRateAndChannelCountValid(format)

Hi, I’m getting an error while starting up speech recognition on an iPad Air 2. Not sure what is causing the issue.

Environment

"device": {
    "family": "iOS",
    "arch": "arm64",
    "storage_size": 63989493760,
    "free_memory": 51675136,
    "memory_size": 2084569088,
    "boot_time": "2017-11-13T07:29:50Z",
    "model": "iPad5,4",
    "usable_memory": 1687126016,
    "type": "device"
},

"os": {
    "rooted": false,
    "kernel_version": "Darwin Kernel Version 17.2.0: Fri Sep 29 18:14:52 PDT 2017; root:xnu-4570.20.62~4/RELEASE_ARM64_T7001",
    "version": "11.1.1",
    "build": "15B150",
    "type": "os",
    "name": "iOS"
}

Exception

Application threw exception com.apple.coreaudio.avfaudio: required condition is false: IsFormatSampleRateAndChannelCountValid(format)

Originated at or in a subcall of folly::Expected<long long, folly::ConversionCode> folly::detail::str_to_integral<long long>(folly::Range<char const*>*)

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 41 (7 by maintainers)

Most upvoted comments

@jamsch I have manage to make it work by adding category and a few function call there. In Voice.m starting from line 38

    NSError* audioSessionError = nil;
    self.audioSession = [AVAudioSession sharedInstance];
    [self.audioSession setCategory:AVAudioSessionCategoryRecord error:&audioSessionError];
    
    if (audioSessionError != nil) {
        [self sendResult:RCTMakeError([audioSessionError localizedDescription], nil, nil) :nil :nil :nil];
        return;
    }
    [self.audioSession setMode:AVAudioSessionModeMeasurement error:&audioSessionError];
    if (audioSessionError != nil) {
        [self sendResult:RCTMakeError([audioSessionError localizedDescription], nil, nil) :nil :nil :nil];
        return;
    }
    [self.audioSession setActive:YES withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&audioSessionError];
    if (audioSessionError != nil) {
        [self sendResult:RCTMakeError([audioSessionError localizedDescription], nil, nil) :nil :nil :nil];
        return;
    }

So AVAudioSession is a singleton/shared instance for your whole app. Changing the category of a shared audio session to AVAudioSessionCategoryRecord will interrupt any audio that is playing. react-native-sound and react-native-audio-toolkit don’t play nicely with this (when AVAudioSessionCategoryPlayback is used) because it seems to either lose the pointer for the audio resource and replace it with the input/microphone source, interrupt the currently playing audio or decreases the volume of the currently playing audio by 80% and all subsequent audio regardless whether speech recognition is active.

react-native-sound has an API option to set the category and this package should too considering that changing it affects other media players. It would then allow for changing the category from Playback to PlayAndRecord when recording, and then back to Playback once done.

Another issue I noticed is that if the startSpeech function were called while speech recognition had already started there’s a return but no callback which results in an unresolved promise if I’m correct.

In my case, the error occurs when I start recognizing immediately after the destroy function. The time destroy function return the promise is not after it truly destroy the recognition. I use setTimeout to delay the restart to about 500 ms to make sure that the recognition is destroyed completely. This solve my problem.

this is probably fixed on master, based on this https://github.com/wenkesj/react-native-voice/commit/fade2bddbec6be35165e8feee7cfa6967b93c669

can anybody confirm this?

Same issue here, this is really holding us back from going into production

Im getting this issue in 0.3.0, is it still not fixed in that version? or is this a regression of something?

fixed on master

I’ll be using react-native-voice on a large production app on iOS quite soon with a fork of this with a few slight fixes. https://github.com/jamsch/react-native-voice/ . Will probably open a few pull requests and clean up the code once everything settles down.

The main differences are:

  • The audio category is reverted back to the original category set prior to using speech recognition
  • Should take bluetooth/wireless sound output devices in to account
  • A method for checking whether speech recognition is ready on iOS.
  • try/catch blocks around code prone to crashing the app, which instead throws an error on the JavaScript side, which allows the user to rather be notified if speech recognition failed.
  • Better error handling/throwing
  • Copied setCategory API from react-native-sound in case the user wants to control the audio category.

@rajeevAgilehub i did fixed my issue by using this package instead. https://github.com/Evgen74/react-native-voice

i think he fixed it same way as yours?

@jamsch I found that the "NSInternalInconsistencyException: SFSpeechAudioBufferRecognitionRequest cannot be re-used" -problem might be caused by a bug in react-navigator which seams to mount the views twice. It works fine when all is setup and initialized, but if the function runs for the first time and the user gets the “Allow microphone” - alert, as soon as they click “Allow”, the app crashes due to this problem. The error occurs when Voice.start() on this line in Voice.m self.recognitionTask = [self.speechRecognizer recognitionTaskWithRequest:self.recognitionRequest resultHandler:^(SFSpeechRecognitionResult * _Nullable result, NSError * _Nullable error) {. I tried to fix the problem by throttle the Voice.start() calls in the view, but the views seams to get mounted from different instances and they calls componentDidMount at the same time so its a bit tricky to get it to work.

Edited I manage to fix this problem by adding a check in the beginning of setupAndStartRecognizing to see if the session already had begun.

- (void) setupAndStartRecognizing:(NSString*)localeStr {
    if(self.sessionId){
        return;
    }

@abhishekbhardwaj Should now be fixed, forgot about returning/breaking out of a switch statement.