Package Exports
- expo-whisper
- expo-whisper/build/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (expo-whisper) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
expo-whisper# expo-whisper
🎤 High-performance speech-to-text transcription for React Native/Expo apps using OpenAI's Whisper model High-performance speech-to-text transcription for React Native/Expo apps using OpenAI's Whisper model
✅ Build Fix (v1.0.10)## Features
This version completely fixes the "Unresolved reference 'whisper'" build errors! The entire package has been rewritten following official Expo Modules API standards.- Real-time transcription with streaming audio support
- Cross-platform - Works on iOS, Android, and Web
🚀 Features- High accuracy using OpenAI's Whisper models
Native performance with C++ integration
✅ Native performance with C++ Whisper integration- Easy integration with React hooks
✅ Cross-platform - Works on iOS and Android- Multi-language support (100+ languages)
✅ Offline capable - No internet required after model loading- Flexible configuration for different use cases
✅ High accuracy using OpenAI's Whisper models- Offline capable - No internet required
✅ Easy integration with React hooks
✅ Expo SDK 51+ compatible## Getting Started
✅ No more build errors - completely fixed!
Prerequisites
📦 Installation
Before you begin, make sure you have:
# Install the package- **Development build** environment set up (expo-whisper cannot run in Expo Go)
npm install expo-whisper- **Android Studio** (for Android builds) or **Xcode** (for iOS builds)
# Install peer dependencies### Step 1: Installation
npx expo install expo-build-properties
``````bash
# Install the package
## ⚙️ Configurationnpm install expo-whisper
Add the plugin to your `app.json`:# Install required peer dependencies
npx expo install expo-build-properties
```json```
{
"expo": {### Step 2: Configure app.json
"name": "Your App",
"plugins": [Add the expo-whisper plugin to your `app.json` configuration:
[
"expo-build-properties",```json
{{
"android": { "expo": {
"minSdkVersion": 24, "name": "Your App",
"compileSdkVersion": 36, "plugins": [
"targetSdkVersion": 36 [
}, "expo-build-properties",
"ios": { {
"deploymentTarget": "11.0" "android": {
} "minSdkVersion": 24,
} "compileSdkVersion": 34,
], "targetSdkVersion": 34,
"expo-whisper" "buildToolsVersion": "34.0.0",
] "enableProguardInReleaseBuilds": true,
} "packagingOptions": {
} "pickFirst": ["**/libc++_shared.so", "**/libjsc.so"]
``` }
},
## 🎯 Basic Usage "ios": {
"deploymentTarget": "11.0"
### Simple API }
}
```tsx ],
import { ExpoWhisper } from 'expo-whisper'; "expo-whisper"
],
// Load a model "android": {
await ExpoWhisper.loadModel('/path/to/ggml-model.bin'); "permissions": [
"android.permission.RECORD_AUDIO",
// Transcribe audio file "android.permission.READ_EXTERNAL_STORAGE",
const text = await ExpoWhisper.transcribe('/path/to/audio.wav'); "android.permission.WRITE_EXTERNAL_STORAGE"
console.log('Transcription:', text); ]
},
// Release model when done "ios": {
await ExpoWhisper.releaseModel(); "infoPlist": {
``` "NSMicrophoneUsageDescription": "This app needs access to microphone for speech recognition."
}
### With React Hook }
}
```tsx}
import { useWhisper } from 'expo-whisper';```
export default function App() {### Step 3: Download a Whisper Model
const {
loadModel,Choose a model based on your performance and accuracy needs:
transcribe,
releaseModel,| Model | Size | Speed | Accuracy | Use Case |
isModelLoaded,|-------|------|-------|----------|----------|
isLoading,| `tiny.en` | 39 MB | Fastest | Basic | Real-time, mobile |
error,| `base.en` | 74 MB | Fast | Good | Mobile apps |
lastResult,| `small.en` | 244 MB | Medium | Better | General purpose |
} = useWhisper();| `medium.en` | 769 MB | Slow | High | High accuracy needed |
const handleLoadModel = async () => {**Download options:**
try {```bash
await loadModel('/path/to/your/model.bin');# Option 1: Download to assets folder
console.log('Model loaded successfully!');mkdir -p assets/models
} catch (error) {curl -L "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en.bin" -o assets/models/ggml-base.en.bin
console.error('Failed to load model:', error);
}# Option 2: Use react-native-fs to download at runtime
};npm install react-native-fs
const handleTranscribe = async () => {
if (!isModelLoaded) {### Step 4: Build Your App
console.warn('Model not loaded');
return;**Important**: expo-whisper requires a development build:
}
try {# For Android
const result = await transcribe('/path/to/audio.wav');npx expo run:android
console.log('Transcription result:', result);
} catch (error) {# For iOS
console.error('Transcription failed:', error);npx expo run:ios
}
};# Or use EAS Build
eas build --platform android --profile development
return (eas build --platform ios --profile development
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>```
<Text>Model loaded: {isModelLoaded ? '✅' : '❌'}</Text>
<Text>Loading: {isLoading ? '⏳' : '✅'}</Text>> **Cannot use Expo Go** - Native libraries require a development build
{error && <Text style={{ color: 'red' }}>Error: {error}</Text>}
{lastResult && <Text>Result: {lastResult}</Text>}### Step 5: Basic Implementation
<Button title="Load Model" onPress={handleLoadModel} />```typescript
<Button title="Transcribe" onPress={handleTranscribe} />import React, { useEffect, useState } from 'react';
</View>import { View, Button, Text, Alert } from 'react-native';
);import { useWhisper } from 'expo-whisper';
}import { Audio } from 'expo-av';
export default function App() {
🔧 API Reference const {
isModelLoaded,
ExpoWhisper Class isTranscribing,
lastResult,
loadModel(modelPath: string): Promise<void>
error,
Load a Whisper model from the given path. loadModel,
transcribeFile,
transcribe(audioPath: string): Promise<string>
clearError,
Transcribe an audio file and return the text. } = useWhisper();
transcribeFile(filePath: string): Promise<string>
const [recording, setRecording] = useState<Audio.Recording>();
Alias for transcribe()
method.
useEffect(() => {
transcribePCM(pcmData: number[], sampleRate?: number): Promise<string>
// Load model when app starts
Transcribe raw PCM audio data (float array). const initializeWhisper = async () => {
try {
isModelLoaded(): boolean
// Path to your downloaded model
Check if a model is currently loaded. await loadModel('/path/to/assets/models/ggml-base.en.bin');
Alert.alert('Success', 'Whisper model loaded successfully!');
releaseModel(): Promise<void>
} catch (error) {
Release the loaded model and free memory. Alert.alert('Error', Failed to load model: ${error.message}
);
}
useWhisper Hook };
Returns an object with: initializeWhisper();
State:
isModelLoaded
,isLoading
,error
,lastResult
}, []);Actions:
loadModel
,transcribe
,transcribeFile
,transcribePCM
,releaseModel
const startRecording = async () => {
📁 Model Setup try {
const permission = await Audio.requestPermissionsAsync();
Download a Whisper model (GGML format): if (permission.status !== 'granted') {
# Example: Base English model (~74MB) return; curl -L "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en.bin" -o ggml-base.en.bin }
await Audio.setAudioModeAsync({
Place in your app's accessible directory: allowsRecordingIOS: true,
iOS: Document Directory or Bundle playsInSilentModeIOS: true,
Android: Internal storage or assets folder });
Use the correct path when calling
loadModel()
const { recording } = await Audio.Recording.createAsync(Audio.RECORDING_OPTIONS_PRESET_HIGH_QUALITY
🏗️ Building );
setRecording(recording);
# Create development build console.error('Failed to start recording', err);
npx expo prebuild --clean }
npx expo run:android };
npx expo run:ios
``` const stopRecording = async () => {
if (!recording) return;
**Note**: This package requires a development build. It cannot run in Expo Go.
setRecording(undefined);
## 🔧 Troubleshooting await recording.stopAndUnloadAsync();
### Build Errors Fixed ✅ const uri = recording.getURI();
if (uri) {
- ✅ "Unresolved reference 'whisper'" - **FIXED in v1.0.10** try {
- ✅ MainApplication.kt compilation errors - **FIXED** const result = await transcribeFile(uri, {
- ✅ Kotlin/Java compatibility issues - **FIXED** language: 'en',
temperature: 0.0,
### Common Issues });
Alert.alert('Transcription', result.text);
1. **Model not loading**: Ensure the model file exists and is accessible } catch (error) {
2. **Audio not transcribing**: Check file format (WAV recommended) and permissions Alert.alert('Error', `Transcription failed: ${error.message}`);
3. **Memory issues**: Release models when not needed with `releaseModel()` }
}
## 📋 Requirements };
- **Expo SDK 51+** or **React Native 0.74+** return (
- **Android**: Min SDK 24, Target SDK 36 <View style={{ flex: 1, padding: 20, justifyContent: 'center' }}>
- **iOS**: Deployment target 11.0+ <Text style={{ fontSize: 18, marginBottom: 20, textAlign: 'center' }}>
- **Development build** (cannot use Expo Go) Whisper Speech-to-Text
</Text>
## 🤝 Contributing
<Text style={{ marginBottom: 10 }}>
1. Fork the repository Model Status: {isModelLoaded ? ' Loaded' : ' Loading...'}
2. Create your feature branch (`git checkout -b feature/amazing-feature`) </Text>
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`) <Button
5. Open a Pull Request title={recording ? 'Stop Recording' : 'Start Recording'}
onPress={recording ? stopRecording : startRecording}
## 📄 License disabled={!isModelLoaded || isTranscribing}
/>
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
{lastResult && (
## 🙏 Acknowledgments <View style={{ marginTop: 20, padding: 10, backgroundColor: '#f0f0f0' }}>
<Text style={{ fontWeight: 'bold' }}>Last Transcription:</Text>
- [OpenAI Whisper](https://github.com/openai/whisper) for the amazing speech recognition model <Text>{lastResult.text}</Text>
- [whisper.cpp](https://github.com/ggerganov/whisper.cpp) for the C++ implementation </View>
- Expo team for the excellent modules framework )}
--- {error && (
<View style={{ marginTop: 20 }}>
**Need help?** [Open an issue](https://github.com/poovarasan4046/expo-whisper/issues) or check our [troubleshooting guide](https://github.com/poovarasan4046/expo-whisper/blob/main/REAL_WORLD_EXAMPLE.md). <Text style={{ color: 'red' }}>Error: {error}</Text>
<Button title="Clear Error" onPress={clearError} />
</View>
)}
</View>
);
}
Step 6: Test Your Implementation
- Build and install your development build
- Grant microphone permissions when prompted
- Tap "Start Recording" and speak clearly
- Tap "Stop Recording" to see transcription results
Configuration Options
Advanced app.json Setup
For production apps, you may want additional configuration:
{
"expo": {
"plugins": [
[
"expo-build-properties",
{
"android": {
"minSdkVersion": 24,
"compileSdkVersion": 34,
"targetSdkVersion": 34,
"proguardMinifyEnabled": true,
"enableProguardInReleaseBuilds": true,
"packagingOptions": {
"pickFirst": [
"**/libc++_shared.so",
"**/libjsc.so",
"**/libfbjni.so"
]
}
},
"ios": {
"deploymentTarget": "11.0",
"bundler": "metro"
}
}
],
[
"expo-whisper",
{
"modelPath": "assets/models/ggml-base.en.bin",
"enableMicrophone": true,
"enableAudioSession": true
}
]
]
}
}
EAS Build Configuration
Create eas.json
for cloud builds:
{
"cli": {
"version": ">= 5.9.0"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"android": {
"buildType": "developmentBuild"
},
"ios": {
"buildConfiguration": "Debug"
}
},
"preview": {
"distribution": "internal",
"android": {
"buildType": "apk"
}
},
"production": {
"android": {
"buildType": "app-bundle"
}
}
}
}
Development Build Required
expo-whisper uses native libraries and cannot run in Expo Go. You must use a development build:
Why Development Build is Required:
- Native Libraries: expo-whisper includes compiled C++ libraries (libwhisper.so)
- Custom Native Code: Direct integration with Whisper C++ implementation
- Platform-specific Optimizations: Hardware-accelerated audio processing
Setting Up Development Build:
# Install development build tools
npx expo install expo-dev-client
# Build for development
npx expo run:android # Local Android build
npx expo run:ios # Local iOS build
# Or use EAS Build (recommended)
eas build --platform android --profile development
eas build --platform ios --profile development
Next Steps
After completing the setup:
- Explore the API: Check out the API Reference section
- Real-time Streaming: Learn about streaming transcription
- Performance Optimization: Read our Audio Guide
- Real-world Examples: See REAL_WORLD_EXAMPLE.md
API Reference
Hooks
useWhisper()
The main hook for managing Whisper transcription state and actions.
Returns:
{
// State
isModelLoaded: boolean;
isLoading: boolean;
isTranscribing: boolean;
error: string | null;
lastResult: WhisperResult | null;
// Actions
loadModel: (modelPath: string) => Promise<void>;
transcribe: (audioPath: string, config?: WhisperConfig) => Promise<WhisperResult>;
transcribeFile: (audioPath: string, config?: WhisperConfig) => Promise<WhisperResult>;
transcribePCM: (pcmData: Uint8Array, config?: WhisperPCMConfig) => Promise<WhisperResult>;
releaseModel: () => Promise<void>;
getModelInfo: () => Promise<string>;
getSupportedFormats: () => Promise<string>;
clearError: () => void;
}
Core API
ExpoWhisper.loadModel(modelPath: string)
Load a Whisper model from the given file path.
ExpoWhisper.transcribeFile(audioPath: string, config?: WhisperConfig)
Transcribe an audio file. Supports WAV, MP3, M4A formats.
ExpoWhisper.transcribePCM(pcmData: Uint8Array, config?: WhisperPCMConfig)
Transcribe raw PCM audio data for real-time streaming.
Configuration Options
WhisperConfig
interface WhisperConfig {
language?: string; // Language code (e.g., 'en', 'es', 'auto')
temperature?: number; // Sampling temperature (0.0 - 1.0)
maxTokens?: number; // Maximum tokens to generate
beamSize?: number; // Beam search size
bestOf?: number; // Number of candidates
patience?: number; // Beam search patience
lengthPenalty?: number; // Length penalty
suppressTokens?: number[]; // Tokens to suppress
initialPrompt?: string; // Initial prompt text
wordTimestamps?: boolean; // Include word-level timestamps
preprendPunctuations?: string;
appendPunctuations?: string;
}
WhisperPCMConfig
interface WhisperPCMConfig {
sampleRate: number; // Audio sample rate (16000 recommended)
channels: number; // Number of channels (1 for mono)
realtime?: boolean; // Enable real-time processing
chunkSize?: number; // Processing chunk size
}
Audio Format Support
Supported Formats:
- WAV (recommended): 16kHz, 16-bit PCM, mono/stereo
- MP3: Various bitrates and sample rates
- M4A: AAC encoded audio
- Raw PCM: For streaming applications
Optimal Settings for Best Performance:
- Sample Rate: 16kHz
- Bit Depth: 16-bit
- Channels: Mono (1 channel)
- Format: WAV or raw PCM for streaming
Advanced Usage
Real-time Streaming
import { useWhisper } from 'expo-whisper';
function StreamingComponent() {
const { transcribePCM } = useWhisper();
const processAudioChunk = async (audioData: Uint8Array) => {
const result = await transcribePCM(audioData, {
sampleRate: 16000,
channels: 1,
realtime: true,
});
console.log('Live transcription:', result.text);
};
}
Batch Processing
const processMultipleFiles = async (filePaths: string[]) => {
const results = [];
for (const path of filePaths) {
try {
const result = await transcribeFile(path, {
language: 'auto',
temperature: 0.0,
});
results.push({ path, text: result.text });
} catch (error) {
results.push({ path, error: error.message });
}
}
return results;
};
Custom Configuration Presets
// High accuracy preset
const highAccuracyConfig = {
temperature: 0.0,
beamSize: 5,
bestOf: 5,
wordTimestamps: true,
};
// Fast processing preset
const fastConfig = {
temperature: 0.1,
beamSize: 1,
bestOf: 1,
maxTokens: 224,
};
// Real-time streaming preset
const streamingConfig = {
sampleRate: 16000,
channels: 1,
realtime: true,
chunkSize: 1024,
};
Platform Support
Platform | Status | Native Library |
---|---|---|
iOS | libwhisper.a | |
Android | libwhisper.so | |
Web | WASM (planned) |
Development
Building from Source
# Clone the repository
git clone https://github.com/poovarasan4046/expo-whisper.git
cd expo-whisper
# Install dependencies
npm install
# Build the package
npm run build
# Run tests
npm test
Native Dependencies
The package includes pre-compiled Whisper libraries:
- Android:
libwhisper.so
(ARM64, ARMv7) - iOS:
libwhisper.a
(ARM64, x86_64)
Contributing
We welcome contributions! Please see our Contributing Guide for details.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
- OpenAI Whisper - The amazing speech recognition model
- whisper.cpp - C++ implementation
- Expo - Development platform
Troubleshooting
Common Build Issues
"Unresolved reference 'whisper'" in MainApplication.kt
Error: Unresolved reference 'whisper'
at line 18 in MainApplication.kt
Cause: This usually happens when:
- You manually imported expo-whisper modules in MainApplication.kt
- There's an incorrect import statement
- The module isn't properly auto-registered
Solution:
- Remove manual imports: Don't manually import expo-whisper in MainApplication.kt
- Use Expo's auto-registration: Let Expo handle module registration automatically
- Check your imports: Remove any lines like:
import expo.modules.whisper // Remove this
- Clean and rebuild:
cd android && ./gradlew clean cd .. && npx expo run:android
minSdkVersion Compatibility
Error: User has minSdkVersion X but library was built for Y
Solution: Ensure your app uses minSdkVersion 24 or higher:
{
"expo": {
"plugins": [
["expo-build-properties", {
"android": {
"minSdkVersion": 24
}
}]
]
}
}
Native Library Loading Issues
Error: UnsatisfiedLinkError: couldn't find libwhisper_jni.so
Solution:
- Ensure you're using a development build (not Expo Go)
- Clean and rebuild the project
- Check that native libraries are properly copied
Getting Help
If you encounter other issues:
- Check our GitHub Issues
- Search for similar problems in the community
- Create a new issue with detailed error logs
Support
Made with for the React Native community