ToneListen React Native SDK
Complete guide to integrating audio tone detection in your React Native applications
Table of Contents
Overview & Features
🎵 Real-time Audio Processing
High-performance audio analysis using Web Audio API with Goertzel algorithm for efficient frequency detection.
🔊 Dual Tone Detection
Recognizes DTMF tone pairs with configurable tolerance and bridge tone validation for sequence accuracy.
📱 Cross-Platform Support
Works seamlessly on iOS, Android, and Web platforms with React Native 0.60+ support.
🎯 In-App Content Delivery
Automatic content display with customizable UI components for images, videos, and web pages.
🔧 TypeScript Support
Full TypeScript definitions included for better development experience and type safety.
🔐 Secure API Integration
Built-in API service with user-specific authentication and client validation.
Technical Specifications
- Algorithm: Goertzel algorithm with Hanning windowing
- Sample Rate: 44.1kHz (configurable)
- Buffer Size: 4800 samples (configurable)
- Tolerance: ±5Hz frequency matching (configurable)
- Platform Support: iOS 11.0+, Android API 21+, React Native 0.60+
Installation & Setup
Step 1: Install the Package
npm install tonelisten-react-native
Step 2: Install Peer Dependencies
npm install react-native-audio-recorder-player react-native-permissions
Step 3: iOS Setup
cd ios && pod install
Step 4: Configure Permissions
Quick Start Guide
1. Basic Setup
import React, { useEffect } from 'react';
import { ToneListenReactNative } from 'tonelisten-react-native';
const App = () => {
useEffect(() => {
const toneListener = new ToneListenReactNative({
clientId: '562', // Your client ID
apiKey: 'your-user-specific-api-key', // Get from admin dashboard
baseURL: 'https://api.toneadmin.com',
debug: true,
enableInAppContent: true,
});
// Initialize and start
toneListener.initialize().then(() => {
toneListener.start();
});
return () => {
toneListener.destroy();
};
}, []);
return <YourAppComponent />;
};
2. Automatic Content Display
import React, { useEffect, useState } from 'react';
import { View } from 'react-native';
import ToneListenReactNative, {
InAppContentModal,
NotificationCenter
} from 'tonelisten-react-native';
const App = () => {
const [modalVisible, setModalVisible] = useState(false);
const [modalContent, setModalContent] = useState(null);
useEffect(() => {
// Set up automatic content display
const notificationCenter = NotificationCenter.getInstance();
notificationCenter.addObserver('InAppContentReceived', (content) => {
setModalContent(content);
setModalVisible(true);
});
// Initialize ToneListen
const toneListener = new ToneListenReactNative({
clientId: '562',
apiKey: 'your-api-key',
baseURL: 'https://api.toneadmin.com',
enableInAppContent: true,
});
toneListener.initialize().then(() => {
toneListener.start();
});
return () => {
toneListener.destroy();
notificationCenter.removeObserver('InAppContentReceived');
};
}, []);
return (
<View style={{ flex: 1 }}>
{/* Your app content */}
<InAppContentModal
visible={modalVisible}
content={modalContent}
onClose={() => setModalVisible(false)}
/>
</View>
);
};
Basic Usage Examples
import React from 'react';
import { View, Text, Button } from 'react-native';
import { useToneDetection } from 'tonelisten-react-native';
const ToneDetectionScreen = () => {
const {
isListening,
isInitialized,
lastSequence,
startListening,
stopListening,
error
} = useToneDetection({
clientId: '562',
apiKey: 'your-api-key',
baseURL: 'https://api.toneadmin.com',
enableInAppContent: true,
onSequence: (event) => {
console.log('Sequence detected:', event.sequence);
}
});
return (
<View style={{ padding: 20 }}>
<Text>Status: {isListening ? 'Listening' : 'Stopped'}</Text>
{lastSequence && <Text>Last Sequence: {lastSequence}</Text>}
{error && <Text>Error: {error.message}</Text>}
<Button
title={isListening ? 'Stop' : 'Start'}
onPress={isListening ? stopListening : startListening}
disabled={!isInitialized}
/>
</View>
);
};
Advanced Features
Define custom tone sequences for specific use cases:
import { CustomToneManager } from 'tonelisten-react-native';
const customToneManager = new CustomToneManager();
// Add custom tone sequence
customToneManager.addCustomSequence({
id: 'custom-sequence-1',
frequencies: [697, 1209], // DTMF '1'
duration: 1000,
callback: (sequence) => {
console.log('Custom sequence detected:', sequence);
// Handle custom sequence
}
});
// Remove custom sequence
customToneManager.removeCustomSequence('custom-sequence-1');
API Reference
constructor(config: ToneListenReactNativeConfig)
Creates a new ToneListenReactNative instance with the specified configuration.
initialize(): Promise<void>
Initializes the tone detection system. Must be called before starting detection.
start(): void
Starts listening for tone sequences. Requires microphone permission.
stop(): void
Stops listening for tone sequences.
updateConfig(config: Partial<ToneListenReactNativeConfig>): void
Updates the configuration of the tone listener.
getState(): ToneDetectionState
Returns the current state of the tone detection system.
destroy(): void
Cleans up resources and stops all detection processes.
Configuration Options
Option | Type | Default | Description |
---|---|---|---|
clientId | string | required | Your client ID from the admin dashboard |
apiKey | string | required | User-specific API key from admin dashboard |
baseURL | string | 'https://api.toneadmin.com' | Base URL for API requests |
sampleRate | number | 44100 | Audio sample rate in Hz |
bufferSize | number | 4800 | Audio buffer size in samples |
tolerance | number | 5 | Frequency matching tolerance in Hz |
bridgeTolerance | number | 10 | Bridge tone tolerance in Hz |
minPower | number | 0.01 | Minimum power threshold |
debug | boolean | false | Enable debug logging |
enableInAppContent | boolean | false | Enable automatic content display |
autoStart | boolean | false | Start automatically after initialization |
Permissions Setup
Troubleshooting
Content Not Displaying
- Check API configuration: Ensure
clientId
,apiKey
, andbaseURL
are correct - Verify NotificationCenter: Make sure you're using
NotificationCenter.getInstance()
(singleton) - Check content type: Ensure your API returns valid
actionType
andactionUrl
- Enable debug logging: Set
debug: true
to see detailed logs
Modal Not Appearing
- Ensure
enableInAppContent: true
is set in configuration - Check that you're listening for
'InAppContentReceived'
notifications - Verify the modal state management in your component
API Calls Failing
- Verify your API key and client ID are correct
- Check network connectivity
- Ensure the API server is accessible
- For web platforms, configure CORS proxy if needed
Permissions Denied
- Ensure microphone permissions are granted
- Check that permission descriptions are properly configured
- Test on physical device (permissions don't work in simulators)
Complete Examples
import React, { useEffect, useState } from 'react';
import {
SafeAreaView,
ScrollView,
StatusBar,
StyleSheet,
Text,
View,
TouchableOpacity,
Alert,
} from 'react-native';
import { useToneDetection } from 'tonelisten-react-native';
const App = () => {
const [detectionHistory, setDetectionHistory] = useState([]);
const handleSequenceDetected = (sequence) => {
setDetectionHistory(prev => [sequence, ...prev.slice(0, 9)]); // Keep last 10
Alert.alert('Tone Sequence Detected', `Sequence: ${sequence}`);
};
const {
isListening,
isInitialized,
lastSequence,
startListening,
stopListening,
error
} = useToneDetection({
clientId: '562',
apiKey: 'your-api-key',
baseURL: 'https://api.toneadmin.com',
enableInAppContent: true,
onSequence: (event) => {
handleSequenceDetected(event.sequence);
},
onDualTone: (result) => {
console.log('Dual tone detected:', result);
},
onBridgeTone: (result) => {
console.log('Bridge tone detected:', result);
},
onError: (err) => {
console.error('Tone detection error:', err);
}
});
return (
<SafeAreaView style={styles.safeArea}>
<StatusBar barStyle="dark-content" />
<ScrollView contentInsetAdjustmentBehavior="automatic">
<View style={styles.header}>
<Text style={styles.headerTitle}>ToneListen React Native</Text>
<Text style={styles.headerSubtitle}>Example App</Text>
</View>
<View style={styles.container}>
<Text style={styles.title}>Tone Detection</Text>
<View style={styles.statusContainer}>
<Text style={styles.statusText}>
Status: {isInitialized ? (isListening ? 'Listening' : 'Stopped') : 'Initializing...'}
</Text>
{error && <Text style={styles.errorText}>Error: {error.message}</Text>}
</View>
<View style={styles.buttonContainer}>
<TouchableOpacity
style={[styles.button, isListening ? styles.stopButton : styles.startButton]}
onPress={isListening ? stopListening : startListening}
disabled={!isInitialized}
>
<Text style={styles.buttonText}>
{isListening ? 'Stop Listening' : 'Start Listening'}
</Text>
</TouchableOpacity>
</View>
{lastSequence && (
<View style={styles.resultContainer}>
<Text style={styles.resultTitle}>Last Sequence:</Text>
<Text style={styles.resultText}>{lastSequence}</Text>
</View>
)}
<View style={styles.historyContainer}>
<Text style={styles.historyTitle}>Detection History:</Text>
{detectionHistory.map((seq, index) => (
<Text key={index} style={styles.historyItem}>
{index + 1}. {seq}
</Text>
))}
</View>
<View style={styles.infoContainer}>
<Text style={styles.infoTitle}>Instructions:</Text>
<Text style={styles.infoText}>
1. Grant microphone permission when prompted{'
'}
2. Tap "Start Listening" to begin tone detection{'
'}
3. Play tone sequences to test detection{'
'}
4. Detected sequences will appear below
</Text>
</View>
</View>
</ScrollView>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
safeArea: {
flex: 1,
backgroundColor: '#f5f5f5',
},
header: {
backgroundColor: '#2196F3',
padding: 20,
alignItems: 'center',
},
headerTitle: {
fontSize: 24,
fontWeight: 'bold',
color: 'white',
},
headerSubtitle: {
fontSize: 16,
color: 'white',
marginTop: 5,
},
container: {
flex: 1,
padding: 20,
},
title: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
marginBottom: 20,
color: '#333',
},
statusContainer: {
marginBottom: 20,
},
statusText: {
fontSize: 16,
textAlign: 'center',
color: '#666',
},
errorText: {
fontSize: 16,
textAlign: 'center',
color: '#ff0000',
marginTop: 10,
},
buttonContainer: {
marginBottom: 20,
},
button: {
padding: 15,
borderRadius: 8,
alignItems: 'center',
},
startButton: {
backgroundColor: '#4CAF50',
},
stopButton: {
backgroundColor: '#f44336',
},
buttonText: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
},
resultContainer: {
marginBottom: 10,
padding: 10,
backgroundColor: 'white',
borderRadius: 8,
borderWidth: 1,
borderColor: '#ddd',
},
resultTitle: {
fontSize: 14,
fontWeight: 'bold',
color: '#333',
marginBottom: 5,
},
resultText: {
fontSize: 16,
color: '#666',
},
historyContainer: {
marginTop: 20,
padding: 15,
backgroundColor: 'white',
borderRadius: 8,
borderWidth: 1,
borderColor: '#ddd',
},
historyTitle: {
fontSize: 16,
fontWeight: 'bold',
color: '#333',
marginBottom: 10,
},
historyItem: {
fontSize: 14,
color: '#666',
marginBottom: 5,
},
infoContainer: {
margin: 20,
padding: 15,
backgroundColor: '#e3f2fd',
borderRadius: 8,
borderLeftWidth: 4,
borderLeftColor: '#2196F3',
},
infoTitle: {
fontSize: 16,
fontWeight: 'bold',
color: '#1976d2',
marginBottom: 10,
},
infoText: {
fontSize: 14,
color: '#1976d2',
lineHeight: 20,
},
});
export default App;