@@ -2,9 +2,65 @@ import React, { useRef, useState, useEffect } from 'react';
22import { StyleSheet , Text , View , TouchableOpacity , ActivityIndicator , Alert } from 'react-native' ;
33import { CameraView , useCameraPermissions , useMicrophonePermissions } from 'expo-camera' ;
44import * as Speech from 'expo-speech' ;
5+ import { Audio } from 'expo-av' ;
56import { MaterialIcons } from '@expo/vector-icons' ;
67import { analyzeImage } from '../services/imageRecognitionService' ;
78import { saveHistoryEntry , loadHistory } from '../services/storageService' ;
9+ import * as FileSystem from 'expo-file-system' ;
10+ import { decode as atob , encode as btoa } from 'base-64' ;
11+
12+ const ELEVENLABS_API_KEY = '' ;
13+ const ELEVENLABS_VOICE_ID = '56AoDkrOh6qfVPDXZ7Pt' ;
14+
15+ function arrayBufferToBase64 ( buffer ) {
16+ let binary = '' ;
17+ const bytes = new Uint8Array ( buffer ) ;
18+ const len = bytes . byteLength ;
19+ for ( let i = 0 ; i < len ; i ++ ) {
20+ binary += String . fromCharCode ( bytes [ i ] ) ;
21+ }
22+ return btoa ( binary ) ;
23+ }
24+
25+ export async function speakWithElevenLabs ( text ) {
26+ try {
27+ const response = await fetch (
28+ `https://api.elevenlabs.io/v1/text-to-speech/${ ELEVENLABS_VOICE_ID } ` ,
29+ {
30+ method : 'POST' ,
31+ headers : {
32+ 'Content-Type' : 'application/json' ,
33+ 'xi-api-key' : ELEVENLABS_API_KEY ,
34+ } ,
35+ body : JSON . stringify ( {
36+ text : text ,
37+ voice_settings : {
38+ stability : 0.5 ,
39+ similarity_boost : 0.75 ,
40+ }
41+ } ) ,
42+ }
43+ ) ;
44+
45+ if ( ! response . ok ) {
46+ console . error ( 'Error from ElevenLabs:' , await response . text ( ) ) ;
47+ return ;
48+ }
49+
50+ // Convert arrayBuffer to base64
51+ const arrayBuffer = await response . arrayBuffer ( ) ;
52+ const base64Audio = arrayBufferToBase64 ( arrayBuffer ) ;
53+ const fileUri = FileSystem . cacheDirectory + `tts-${ Date . now ( ) } .mp3` ;
54+ await FileSystem . writeAsStringAsync ( fileUri , base64Audio , { encoding : FileSystem . EncodingType . Base64 } ) ;
55+
56+ const soundObject = new Audio . Sound ( ) ;
57+ await soundObject . loadAsync ( { uri : fileUri } ) ;
58+ await soundObject . playAsync ( ) ;
59+
60+ } catch ( error ) {
61+ console . error ( 'Error:' , error ) ;
62+ }
63+ }
864
965export default function CameraScreen ( { navigate } ) {
1066 const cameraRef = useRef ( null ) ;
@@ -15,7 +71,7 @@ export default function CameraScreen({ navigate }) {
1571 const [ ttsEnabled , setTtsEnabled ] = useState ( true ) ;
1672 const [ autoCapture , setAutoCapture ] = useState ( false ) ;
1773 const intervalRef = useRef ( null ) ;
18-
74+
1975 // Load history count for badge
2076 useEffect ( ( ) => {
2177 refreshHistoryCount ( ) ;
@@ -74,7 +130,7 @@ export default function CameraScreen({ navigate }) {
74130 // TTS wrapper
75131 const speakIfEnabled = ( text ) => {
76132 if ( ttsEnabled ) {
77- Speech . speak ( text , { rate : 0.9 , pitch : 1.0 } ) ;
133+ speakWithElevenLabs ( text ) ;
78134 }
79135 } ;
80136
0 commit comments