jamsch / expo-speech-recognition

Speech Recognition for React Native Expo projects
MIT License
128 stars 11 forks source link

_expoSpeechRecognition.ExpoSpeechRecognitionModule.start is not a function #33

Closed JeanBaptisteBolh closed 1 month ago

JeanBaptisteBolh commented 1 month ago

Hi all this library looks awesome, but I'm unable to get the start event listener to work. End is working fine though. I'm using expo ~51.0.28 with react-native 0.74.5

Here is my code:

import React, { useState } from "react";
import {
  ScrollView,
  Text,
  TouchableOpacity,
  TextInput,
  View,
  StyleSheet,
} from "react-native";
import { MenuOption } from "types/MenuOption";
import * as Location from "expo-location";
import { useRouter } from "expo-router";
import { Iconify } from "react-native-iconify"; // Import the icon component
import { COLORS } from "contants/Colors";
import {
  ExpoSpeechRecognitionModule,
  useSpeechRecognitionEvent,
} from "expo-speech-recognition";

const OptionList = ({ items }: { items: MenuOption[] }) => {
  const router = useRouter();
  const [search, setSearch] = useState("");
  const [recognizing, setRecognizing] = useState(false);
  const [transcript, setTranscript] = useState("");

  useSpeechRecognitionEvent("start", () => {
    console.log("start");
    setRecognizing(true);
  });
  useSpeechRecognitionEvent("end", () => {
    console.log("end");
    setRecognizing(false);
  });
  useSpeechRecognitionEvent("result", (event) => {
    console.log("result");
    setTranscript(event.results[0]?.transcript);
  });
  useSpeechRecognitionEvent("error", (event) => {
    console.log("error code:", event.error, "error messsage:", event.message);
  });

  const handleStart = async () => {
    console.log("Handling start");
    const result = await ExpoSpeechRecognitionModule.requestPermissionsAsync();
    if (!result.granted) {
      console.warn("Permissions not granted", result);
      return;
    }
    // Start speech recognition
    ExpoSpeechRecognitionModule.start({
      lang: "en-US",
      interimResults: true,
      maxAlternatives: 1,
      continuous: false,
      requiresOnDeviceRecognition: false,
      addsPunctuation: false,
      contextualStrings: ["Carlsen", "Nepomniachtchi", "Praggnanandhaa"],
    });
  };

  const handleStop = () => {
    ExpoSpeechRecognitionModule.stop();
  };

  const handlePressOption = async (itemTitle: string) => {
    // Get current location
    const { status } = await Location.requestForegroundPermissionsAsync();
    if (status !== "granted") {
      console.log("Permission to access location was denied");
      return;
    }

    const location = await Location.getCurrentPositionAsync({});
    const { latitude, longitude } = location.coords;

    // Navigate to PlacesList screen with query and location
    router.push({
      pathname: "/placeslist",
      params: {
        query: itemTitle,
        lat: latitude,
        lng: longitude,
      },
    });
  };

  const filteredItems = items.filter((item) =>
    item.title.toLowerCase().includes(search.toLowerCase())
  );

  return (
    <View style={{ flex: 1, marginTop: 10 }}>
      <TextInput
        placeholder="Search..."
        onChangeText={setSearch}
        value={search}
        style={{
          height: 50,
          borderColor: "lightgrey",
          borderWidth: 1,
          borderRadius: 10,
          marginHorizontal: 10,
          paddingHorizontal: 10,
          marginBottom: 10,
          fontSize: 20, // Larger font size
          fontFamily: "GabaritoRegular", // Use GabaritoRegular font
        }}
      />
      <ScrollView contentContainerStyle={{ paddingBottom: 120 }}>
        {filteredItems.length > 0 ? (
          filteredItems.map((item, index) => (
            <TouchableOpacity
              key={index}
              onPress={() => handlePressOption(item.title)}
              style={{
                flexDirection: "row",
                alignItems: "center",
                marginHorizontal: 10,
                padding: 10,
              }}
            >
              {item.icon}
              <Text style={{ fontFamily: "GabaritoRegular", fontSize: 26 }}>
                {item.title}
              </Text>
            </TouchableOpacity>
          ))
        ) : (
          <TouchableOpacity
            onPress={() => handlePressOption(search)}
            style={{
              flexDirection: "row",
              alignItems: "center",
              marginHorizontal: 10,
              padding: 10,
            }}
          >
            <Iconify
              icon={"mdi:magnify"}
              size={26}
              style={{ marginRight: 10 }}
            />
            <Text style={{ fontFamily: "GabaritoRegular", fontSize: 26 }}>
              Find: {search}
            </Text>
          </TouchableOpacity>
        )}
      </ScrollView>
      <TouchableOpacity
        style={styles.micButton}
        onPressIn={handleStart}
        onPressOut={handleStop}
      >
        <Iconify icon={"mdi:microphone"} size={50} color={"white"} />
      </TouchableOpacity>
    </View>
  );
};

const styles = StyleSheet.create({
  micButton: {
    position: "absolute",
    bottom: 35,
    alignSelf: "center",
    backgroundColor: COLORS.primaryRed,
    borderRadius: 50,
    width: 80,
    height: 80,
    justifyContent: "center",
    alignItems: "center",
    elevation: 5,
  },
});

export default OptionList;
jamsch commented 1 month ago

Hey @JeanBaptisteBolh, the error in the title that you're describing seems to be the ExpoSpeechRecognitionModule.start({...}) call. An issue like that is usually to do with the app not being re-built with the expo-speech-recognition native code (since this app contains native Kotlin and Swift code that needs to be built with the app). Have you ran npx expo run:ios / npx expo run:android first?

JeanBaptisteBolh commented 1 month ago

@jamsch Thank you for your response.

I ran the following commands:

npx expo start
npx expo run:ios --device

I get a console warning saying this when I click the button I have to listen to my speech: "Possible unhandled promise rejection (id: 3)": TypeError: _expoSpeechRecognition.ExpoSpeechRecognitionModule.start({ lang: "en-US", interimResults: true, maxAlternatives: 1, continuous: false, requiresOnDeviceRecognition: false, addsPunctuation: false, contextualStrings: ["Carlsen", "Nepomniachtchi", "Praggnanandhaa"], }) '_expoSpeechRecognition.ExpoSpeechRecognitionModule.start' is undefined

jamsch commented 1 month ago

@JeanBaptisteBolh I can't see anything syntactically wrong with your code. Just a few questions:

JeanBaptisteBolh commented 1 month ago

I am using "expo": "~51.0.28"

It does happen after I press the microphone button

The output of result is this: {"canAskAgain": true, "expires": "never", "granted": true, "status": "granted"}

Here are all of my dependencies: "dependencies": { "axios": "^1.7.7", "babel-plugin-module-resolver": "^5.0.2", "expo": "~51.0.28", "expo-constants": "~16.0.2", "expo-font": "~12.0.10", "expo-linking": "~6.3.1", "expo-location": "~17.0.1", "expo-router": "~3.5.23", "expo-speech-recognition": "^0.2.21", "expo-status-bar": "~1.12.1", "react": "18.2.0", "react-dom": "18.2.0", "react-native": "0.74.5", "react-native-iconify": "^1.0.2", "react-native-maps": "1.14.0", "react-native-safe-area-context": "4.10.5", "react-native-screens": "3.31.1", "react-native-svg": "15.2.0", "react-native-web": "~0.19.10" },

jamsch commented 1 month ago

@JeanBaptisteBolh Can you also console.log(ExpoSpeechRecognitionModule)? What you should be getting is the following:

(NOBRIDGE) LOG {"abort": [Function abort], "addListener": [Function addListener], "emit": [Function emit], "getAssistantService": [Function getAssistantService], "getAudioSessionCategoryAndOptionsIOS": [Function getAudioSessionCategoryAndOptionsIOS], "getDefaultRecognitionService": [Function getDefaultRecognitionService], "getPermissionsAsync": [Function getPermissionsAsync], "getSpeechRecognitionServices": [Function getSpeechRecognitionServices], "getStateAsync": [Function getStateAsync], "getSupportedLocales": [Function getSupportedLocales], "isRecognitionAvailable": [Function isRecognitionAvailable], "listenerCount": [Function listenerCount], "removeAllListeners": [Function removeAllListeners], "removeListener": [Function removeListener], "removeSubscription": [Function removeSubscription], "requestPermissionsAsync": [Function requestPermissionsAsync], "setAudioSessionActiveIOS": [Function setAudioSessionActiveIOS], "setCategoryIOS": [Function setCategoryIOS], "start": [Function start], "stop": [Function stop], "supportsOnDeviceRecognition": [Function supportsOnDeviceRecognition], "supportsRecording": [Function supportsRecording]}

JeanBaptisteBolh commented 1 month ago

@jamsch Thank you for your help. I put this on pause and was working on a bunch of other stuff (which included installing some other packages). For some reason after I did that and came back to this and rebuilt/tested, everything is working fine.

Not sure why this would affect things and hopefully nobody else will encounter this issue but...

These were the dependencies I added to my project: @react-native-async-storage/async-storage: "1.23.1" @reduxjs/toolkit: "^2.2.7" react-native-google-mobile-ads: "^14.2.3" react-native-purchases: "^8.2.2" expo-tracking-transparency: "~4.0.2" react-redux: "^9.1.2"

levytskyy commented 1 month ago

the same error

shariqwaseem commented 2 weeks ago

I am also encountering this issue. Have tried prebuild --clean. Removing node_modules and doing yarn again. Still same

jamsch commented 2 weeks ago

@shariqwaseem Just to verify: have you re-built your app (i.e. running npx expo run:ios / android) after running npx expo prebuild --clean? If so and it's still not working, could you provide a repository so I could test this out?