voice: Recognition and Bluetooth Headset : Error required condition is false : length <= _imp->_frameCapacity on production

Bug

When I start recognising I get this error : EDIT : it occurs when my phone is connected to a bluetooth headset !!

img_1605

Environment info

Device on production: iPhone X with iOS : 14.1 Xcode : Version 12.1

npmPackages:

    "@react-native-community/voice": "^1.1.9", 
    "react": "16.13.1",
    "react-native": "0.63.2",

and many other whose “react-native-exception-handler”: “^2.10.9”.

Reproducible sample code

create this hooks useRecognition.js

import React, {useState, useEffect, useRef} from 'react';
import {Platform} from 'react-native';
import Voice from '@react-native-community/voice';
import {useSelector} from 'react-redux';

const ERROR_MSG_NO_SPEECH = Platform.OS === 'ios' ? '203/Retry' : '7/No match';

export const useRecognition = ({setSpeechIsRunning, setVoiceIsAvailable}) => {
  const {lang} = useSelector((state) => state.settings); // One of this string values : 'fr, 'en, 'ar'.

  const [error, setError] = useState(null);
  const [results, setResults] = useState([]);
  const autoStopTimeout = useRef(null);

  useEffect(() => {
    // Attribute function to all listeners of Voice library
    Voice.onSpeechStart = onSpeechStart;
    Voice.onSpeechEnd = onSpeechEnd;
    Voice.onSpeechError = onSpeechError;
    Voice.onSpeechResults = onSpeechResults;
    Voice.onSpeechPartialResults = onSpeechPartialResults;
    Voice.onSpeechRecognized = onSpeechRecognized;
    Voice.onSpeechVolumeChanged = onSpeechVolumeChanged;

    if (error?.message && error.message !== ERROR_MSG_NO_SPEECH) {
      setError(null);
      setVoiceIsAvailable(false);
      Voice.destroy().then(Voice.removeAllListeners());
    }
    return function cleanUpListeners() {
      // Component will unmount
      Voice.destroy().then(Voice.removeAllListeners());
      clearTimeout(autoStopTimeout.current);
    };
  }, [error]);

  // HANDLER FUNCTIONS

  const resetAutoStopTimeout = () => {
    clearTimeout(autoStopTimeout.current);
    let timeout = setTimeout(async () => {
      await Voice.stop();
      setSpeechIsRunning(false);
    }, 1000);
    autoStopTimeout.current = timeout;
  };
  const startAutoStopTimeout = () => {
    let timeout = setTimeout(async () => {
      await Voice.stop();
      setSpeechIsRunning(false);
    }, 4000);
    autoStopTimeout.current = timeout;
  };

  const onSpeechError = async (e) => {
    setError(e.error);
    await Voice.destroy();
  };

  const onSpeechVolumeChanged = () => {};

  const onSpeechRecognized = (e) => {
    resetAutoStopTimeout();
  };

  const onSpeechStart = (e) => {
    startAutoStopTimeout();
  };

  const onSpeechEnd = (e) => {};

  const onSpeechResults = (e) => {
    setResults(e.value);
  };

  const onSpeechPartialResults = (e) => {};

  const checkRecognition = async (e) => {
    const isAvailable = await Voice.isAvailable();
    const isRecognizing = await Voice.isRecognizing();
    let speechServicesAndroid = undefined;
    if (Platform.OS === 'android') {
      speechServicesAndroid = await Voice.getSpeechRecognitionServices();
    }
    return {isAvailable, isRecognizing, speechServicesAndroid};
  };

  // ACTION FUNCTIONS
  const startRecognition = async () => {
    try {
      await Voice.start(lang);
      // await Voice.start('fr-FR');
    } catch (error) {
      console.error('Error on start recognization : ', error);
      setError(error);
    }
  };

  const stopRecognition = async () => {
    try {
      await Voice.stop();
    } catch (error) {
      console.error('Error on stop recognization : ', error);
      setError(error);
    }
  };

  return {
    startRecognition,
    stopRecognition,
    checkRecognition,
    results,
    error,
  };
};

I call this hooks in an other component like this :

import React, {useState, useEffect, useCallback, useRef} from 'react';
import { View, Button } from 'react-native';
import {useRecognition} from '../hooks';

// ....

const MyOtherComponent = () => { 
// ...
  const [speechIsRunning, setSpeechIsRunning] = useState(true);
  const [voiceIsAvailable, setVoiceIsAvailable] = useState(true);

// ...
  const {
    startRecognition,
    stopRecognition,
    checkRecognition,
    results,
  } = useRecognition({setSpeechIsRunning, setVoiceIsAvailable});

useEffect(() => {
    if (speechIsRunning) {
      // Start
      startRecognition();
      checkVoiceAvailable();
    } else {
      // Stop
      stopRecognition();
    }
  }, [speechIsRunning]);

const checkVoiceAvailable = useCallback(async () => {
    // Check if voice recognition is available
    const {isAvailable, speechServicesAndroid} = await checkRecognition();
    if (speechIsRunning) {
      if (
        !isAvailable ||
        (Platform.OS === 'android'
          ? speechServicesAndroid?.length === 0
          : false)
      ) {
        setSpeechIsRunning(false);
        stopRecognition();
        setVoiceIsAvailable(false);
      }
    }
  }, [speechIsRunning]);

return (
    <View style={{flex : 1}}>
      <Button
        onPress={onPressLearnMore}
        title="Stop Reco"
      />
    </View>
  );
};

export default MyOtherComponent

EDIT : And you have to connect a bluetooth headset on your phone.

Thank you in advance !!!

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 3
  • Comments: 25 (13 by maintainers)

Commits related to this issue

Most upvoted comments

This was released with 2.0.1! https://github.com/react-native-voice/voice/pull/294

Closing this issue for now, let me know if the issue still persists with the update. Reminder with v2 the package was moved to @react-native-voice/voice

@mataspetrikas Yes, I think you are correct I’ve been testing it for a couple weeks with a simple one line change that prevents this crash by setting the frame length to be the same as the frame capacity rather than the 4096 hard coded value. @safaiyeh I just submitted a pull request #294 for this bug fix could you take a look at merging it?

Really appreciate this, I’ll check it out later today

@agliber I had to refuse users from using bluetooth headset as a workaround ((