expo-speech-recognition

repository·main·Indexed 20 days ago

https://github.com/jamsch/expo-speech-recognition

A unified API for speech recognition in React Native Expo projects, supporting iOS (SFSpeechRecognizer), Android (SpeechRecognizer), and Web (SpeechRecognition). It provides a React hook (useSpeechRecognitionEvent) and a direct module API (ExpoSpeechRecognitionModule) to manage recognition lifecycles, permissions, and audio transcription. Features include support for interim results, on-device recognition, and the ability to persist audio recordings to local files or transcribe existing audio files on Android 13+ and iOS.

Tokens
21.3K
Snippets
53
Records
66
Agent score
70%

What's inside expo-speech-recognition

  1. Understand the architecture of expo-speech-recognition

    main

    The library operates through a layered architecture that bridges JavaScript to native speech engines:

    1. JavaScript Layer: Provides the useSpeechRecognitionEvent hook and the ExpoSpeechRecognitionModule. It also includes ExpoWebSpeechRecognition to polyfill external libraries that rely on the Web Speech API.
    2. Platform Bridges: Uses Expo Modules Core to communicate between JS and native code.
    3. Native Implementations:
      • iOS: Uses SFSpeechRecognizer and AVAudioEngine via ExpoSpeechRecognitionModule.swift.
      • Android: Uses Android SpeechRecognizer and ExpoAudioRecorder via ExpoSpeechRecognitionModule.kt.
      • Web: Uses ExpoSpeechRecognitionModule.web.ts.
  2. Understand the event flow for speech recognition

    main

    The library emits several events during the speech recognition lifecycle. The flow differs slightly depending on whether you are using non-continuous or continuous recognition.

    Non-continuous recognition flow

    1. Initialization: start(options) is called, triggering start and audiostart events.
    2. Recognition Loop:
      • volumechange events are emitted (only if the user has opted in).
      • result events with {isFinal: false} are emitted for partial results (only if interimResults is enabled).
    3. Completion:
      • A final result event is emitted with {isFinal: true}.
      • audioend and end events are emitted to signal the process is finished.

    Continuous recognition flow

    In continuous mode, the engine processes multiple segments:

    1. Interim Results: Volatile result events (isFinal: false) are emitted for new segments. You may need to concatenate these with previous final results.
    2. Final Results: result events with {isFinal: true} are emitted for completed utterances/segments.
    3. Completion: The process concludes with audioend and end events.
  3. Persist audio recordings to local files

    main

    You can save the audio captured during speech recognition by enabling recordingOptions.persist in the start() method. When enabled, the module emits { uri: string } in the audiostart and audioend events, providing the local file path.

    Important Requirements:

    • Available on Android 13+ and iOS.
    • Always call supportsRecording() to verify availability before attempting to use this feature.
    • Do not attempt to access the file until the audioend event has been emitted.

    Default Output Formats:

    • Android: Linear PCM (16000 Hz, mono).
    • iOS: 32-bit Float PCM (44100/48000 Hz, mono). You can customize this using recordingOptions.outputSampleRate and recordingOptions.outputEncoding.
    import { useState } from "react";
    import {
      ExpoSpeechRecognitionModule,
      useSpeechRecognitionEvent,
    } from "expo-speech-recognition";
    
    function RecordAudio() {
      const [recording, setRecording] = useState(false);
      const [recordingUri, setRecordingUri] = useState<string | null>(null);
    
      const handleStart = () => {
        setRecording(true);
        ExpoSpeechRecognitionModule.start({
          lang: "en-US",
          recordingOptions: {
            persist: true,
            outputDirectory: "/path/to/directory",
            outputFileName: "recording.wav",
            outputSampleRate: 16000, // iOS only
            outputEncoding: "pcmFormatInt16", // iOS only
          },
        });
      };
    
      useSpeechRecognitionEvent("audiostart", (event) => {
        console.log("Recording started for file:", event.uri);
      });
    
      useSpeechRecognitionEvent("audioend", (event) => {
        console.log("Local file path:", event.uri);
        setRecordingUri(event.uri);
      });
    
      // ... render logic
    }
  4. Set up the expo-speech-recognition example app

    main

    To run the example application, you must install dependencies for both the example folder and the root repository, prepare the library, and then build the native platforms. Follow these steps in order:

    1. Install dependencies: Run npm install in the example directory.
    2. Install root dependencies: Navigate to the root folder and run npm install.
    3. Prepare the library: Run npm run prepare from the root to build the expo-speech-recognition library.
    4. Build and run: Navigate back to the example folder to build for iOS (npm run ios), Android (npm run android), or start the Metro bundler (npm start).
    # Install dependencies
    npm install
    
    # Also install dependencies of the root folder
    cd ../
    npm install
    
    # Build the expo-speech-recognition library
    npm run prepare
    
    # Go back to the example folder and build the app
    cd example
    
    # Build the iOS app
    npm run ios
    # Build the Android app
    npm run android
    
    # Run the Metro JS bundling server
    npm start
  5. Enable volume metering

    main

    To provide visual feedback (like a volume meter) during speech recognition, enable volumeChangeEventOptions.enabled. This emits a volumechange event containing a value between -2 and 10, where values <= 0 are considered inaudible.

    Options:

    • enabled: Boolean to turn metering on/off.
    • intervalMillis: Frequency of the events in milliseconds.
    import { ExpoSpeechRecognitionModule, useSpeechRecognitionEvent } from "expo-speech-recognition";
    
    function VolumeMeteringExample() {
      useSpeechRecognitionEvent("volumechange", (event) => {
        // value is between -2 and 10
        console.log("Volume changed to:", event.value);
      });
    
      const handleStart = () => {
        ExpoSpeechRecognitionModule.start({
          lang: "en-US",
          volumeChangeEventOptions: {
            enabled: true,
            intervalMillis: 300,
          },
        });
      };
    }
  6. Manage iOS audio session conflicts

    main

    Because this library modifies the audio session category and mode, it may conflict with multimedia applications (audio/video playback). To integrate seamlessly, use these APIs:

    • Retrieve current state: Use getAudioSessionCategoryAndOptionsIOS() before starting recognition to capture existing settings.
    • Configure on start: Pass the iosCategory option to ExpoSpeechRecognitionModule.start({ iosCategory }).
    • Manual updates: Use setAudioCategoryIOS({ category, categoryOptions, mode }) to change the session state at a later time.
  7. Transcribe existing audio files

    main

    Instead of using the microphone, you can transcribe pre-recorded audio files by providing a URI in the audioSource.uri option within start().

    Key Considerations:

    • Availability: Requires Android 13+ or iOS. If unsupported, an error event with code audio-capture is emitted.
    • On-Device Recognition: For long-form audio, use requiresOnDeviceRecognition: true to avoid network latency/costs. On Android, check getSupportedLocales() first to ensure the speech model is installed.
    • Android Configuration: You must specify audioChannels, audioEncoding (using AudioEncodingAndroid), and sampleRate for the source file.
    • Chunk Delay: Use chunkDelayMillis to manage the flow of audio chunks to the service. Default is 50ms for network-based and 15ms for on-device recognition.
    import { Platform } from "react-native";
    import {
      ExpoSpeechRecognitionModule,
      useSpeechRecognitionEvent,
      AudioEncodingAndroid,
    } from "expo-speech-recognition";
    
    function TranscribeAudioFile() {
      const handleTranscribe = () => {
        ExpoSpeechRecognitionModule.start({
          lang: "en-US",
          interimResults: true,
          requiresOnDeviceRecognition: Platform.OS === "ios",
          audioSource: {
            uri: "file:///path/to/audio.wav",
            audioChannels: 1,
            audioEncoding: AudioEncodingAndroid.ENCODING_PCM_16BIT,
            sampleRate: 16000,
            chunkDelayMillis: undefined,
          },
        });
      };
    
      useSpeechRecognitionEvent("result", (ev) => {
        // Note: multiple final results may be returned; concatenate as needed
        console.log(ev.results[0]?.transcript);
      });
    }
  8. Install expo-speech-recognition

    main

    To use expo-speech-recognition in your React Native project, install the package via npm. If you are using an older Expo SDK, specify the version accordingly.

    npm install expo-speech-recognition
    
    # For older SDKs:
    npm install expo-speech-recognition@sdk-54
    npm install expo-speech-recognition@sdk-53
    npm install expo-speech-recognition
  9. Enable on-device speech recognition on Android

    main

    To use on-device recognition on Android 13+, you must ensure a locale model is installed:

    1. Call getSupportedLocales() to check for installed locales. If none are listed, you must download one.
    2. Call androidTriggerOfflineModelDownload() for the specific locale you wish to use.
    3. If issues persist, verify the locale installation via the system settings: Settings -> Security and privacy -> More privacy settings -> Android System Intelligence -> On-device speech recognition (path may vary).
    // Example workflow for on-device recognition:
    const locales = await ExpoSpeechRecognitionModule.getSupportedLocales();
    if (locales.length === 0) {
      await ExpoSpeechRecognitionModule.androidTriggerOfflineModelDownload('en-US');
    }
  10. Use language detection on Android 14+

    main

    Language detection is available on Android 14+ using the com.google.android.as service package. To use it, you must enable EXTRA_ENABLE_LANGUAGE_DETECTION in androidIntentOptions and select the correct service package.

    Requirements:

    • Android 14+ only.
    • Use androidRecognitionServicePackage: "com.google.android.as" (or set requiresOnDeviceRecognition: true).
    • Enable EXTRA_ENABLE_LANGUAGE_DETECTION in androidIntentOptions.
    • (Optional) Enable EXTRA_ENABLE_LANGUAGE_SWITCH to allow language switching, but ensure the required language model is downloaded via androidTriggerOfflineModelDownload().
    import { useSpeechRecognitionEvent, ExpoSpeechRecognitionModule } from "expo-speech-recognition";
    
    useSpeechRecognitionEvent("languagedetection", (event) => {
      console.log("Language detected:", event.detectedLanguage); // e.g. "en-us"
      console.log("Confidence:", event.confidence); // A value between 0.0 and 1.0
      console.log("Top locale alternatives:", event.topLocaleAlternatives); // e.g. ["en-au", "en-gb"]
    });
    
    // Start recognition
    ExpoSpeechRecognitionModule.start({
      androidIntentOptions: {
        EXTRA_ENABLE_LANGUAGE_DETECTION: true,
        EXTRA_ENABLE_LANGUAGE_SWITCH: true,
      },
      androidRecognitionServicePackage: "com.google.android.as", // or set "requiresOnDeviceRecognition" to true
    });