UNPKG

@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
"use strict"; 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