@revrag-ai/embed-react-native
Version:
A powerful React Native library for integrating AI-powered voice agents into mobile applications. Features real-time voice communication, intelligent speech processing, customizable UI components, and comprehensive event handling for building conversation
152 lines (139 loc) • 5.18 kB
JavaScript
;
import { useEffect, useRef, useState } from 'react';
import { View, Animated } from 'react-native';
import { jsx as _jsx } from "react/jsx-runtime";
// React Native compatible waveform simulator
const useReactNativeAudioWaveform = roomRef => {
const [isAudioActive, setIsAudioActive] = useState(false);
const intervalRef = useRef(null);
const [currentHeights, setCurrentHeights] = useState(Array(10).fill(0));
// Create animated values for each bar
const barCount = 10;
const animatedBars = useRef(Array(barCount).fill(0).map(() => new Animated.Value(0))).current;
useEffect(() => {
// Check if there's an active room connection AND if agent is talking
const checkAudioActivity = () => {
const room = roomRef.current;
if (room?.state !== 'connected') {
setIsAudioActive(false);
return;
}
// Check if any remote participant is currently speaking
let isAgentSpeaking = false;
// Loop through all remote participants
room.remoteParticipants.forEach(participant => {
const audioTrackPublications = Array.from(participant.getTrackPublications().values());
const remoteAudioTrack = audioTrackPublications.find(pub => pub.track?.kind === 'audio');
// Check if this participant has audio track, is not muted, and is actively speaking
if (remoteAudioTrack?.track && !remoteAudioTrack?.isMuted) {
// Check audio level to detect actual speech (optional but more accurate)
const audioLevel = participant.audioLevel || 0;
if (audioLevel > 0.05) {
// Threshold for detecting speech
isAgentSpeaking = true;
}
}
});
setIsAudioActive(isAgentSpeaking);
};
// Initial check
checkAudioActivity();
// Set up periodic checking for room state changes
intervalRef.current = setInterval(checkAudioActivity, 500);
// Clean up on unmount
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
setIsAudioActive(false);
};
}, [roomRef]);
// Continuous smooth animation
useEffect(() => {
const animateWaveform = () => {
// Generate smooth waveform data - stop animation completely when not active
const targetHeights = isAudioActive ? Array(barCount).fill(0).map((_, index) => {
const timeOffset = Date.now() / 800 + index * 0.3;
const baseHeight = 0.5;
const amplitude = 0.5;
const height = baseHeight + amplitude * Math.abs(Math.sin(timeOffset));
return Math.max(0.1, Math.min(1.0, height));
}) : Array(barCount).fill(0); // Completely freeze animation when mic is muted
// Update current heights for conditional logic
setCurrentHeights(targetHeights);
const animations = animatedBars.map((animatedValue, index) => {
const targetHeight = targetHeights[index] || 0;
return Animated.timing(animatedValue, {
toValue: targetHeight,
duration: isAudioActive ? 400 : 600,
// Slower fade out when going inactive
useNativeDriver: false
});
});
Animated.parallel(animations).start();
};
// Start animation immediately and repeat
animateWaveform();
const animationInterval = setInterval(animateWaveform, 300);
return () => {
clearInterval(animationInterval);
};
}, [isAudioActive, animatedBars]);
return {
animatedBars,
currentHeights,
isActive: isAudioActive
};
};
export const WaveformVisualizer = ({
roomRef
}) => {
const {
animatedBars,
currentHeights
} = useReactNativeAudioWaveform(roomRef);
console.log('animatedBars', animatedBars);
return /*#__PURE__*/_jsx(View, {
style: {
flexDirection: 'row',
alignItems: 'center',
height: '100%',
alignSelf: 'center',
// width: '100%',
// flex: 1,
justifyContent: 'center',
// position: 'absolute',
zIndex: 1000
},
children: animatedBars.map((animatedHeight, idx) => {
// Use the tracked height values instead of trying to access animated value directly
const currentHeightValue = currentHeights[idx] || 0.1;
// Apply conditional logic based on height
let conditionalValue;
if (currentHeightValue > 0.7) {
conditionalValue = 1;
} else if (currentHeightValue >= 0.4 && currentHeightValue <= 0.5) {
conditionalValue = 5;
} else {
conditionalValue = 1;
}
// You can use conditionalValue for width, color, or other properties
return /*#__PURE__*/_jsx(Animated.View, {
style: {
width: conditionalValue === 10 ? 4 : 4,
borderRadius: 100,
// Example: wider bars for value 5
height: animatedHeight.interpolate({
inputRange: [0, 1],
outputRange: [0, 25]
}),
alignSelf: 'center',
backgroundColor: idx <= 1 || idx >= 8 ? 'rgba(255, 255, 255, 0.5)' : 'white',
margin: 1.5
}
}, idx);
})
});
};
//# sourceMappingURL=EmbedAudioWave.js.map