import { useState, useEffect } from 'react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Textarea } from '@/components/ui/textarea'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Badge } from '@/components/ui/badge'; import { Play, Pause, Download, Volume2, Activity } from 'lucide-react'; import { useToast } from '@/hooks/use-toast'; import api from '@/services/api'; import SpeakerScene from '../three/SpeakerScene'; import AudioWaveform from '../audio/AudioWaveform'; import MelSpectrogramVisualizer from '../audio/MelSpectrogramVisualizer'; import ProcessingPipeline from '../audio/ProcessingPipeline'; import FFTVisualizer from '../audio/FFTVisualizer'; import RealTimeStatsDashboard from '../audio/RealTimeStatsDashboard'; interface Voice { id: string; name: string; audioUrl?: string; } interface SpeechSynthesisProps { voices?: Voice[]; language?: 'english' | 'hindi'; onLanguageChange?: (language: 'english' | 'hindi') => void; onSynthesisComplete?: (audioUrl: string) => void; className?: string; } // Sample texts for different languages const sampleTexts = { english: "Hello, this is a sample text for speech synthesis. The technology can convert this text into natural-sounding speech.", hindi: "नमस्ते, यह स्पीच सिंथेसिस के लिए एक नमूना टेक्स्ट है। यह तकनीक इस टेक्स्ट को प्राकृतिक आवाज़ में बदल सकती है।", mixed: "Hello दोस्तों, this is a mixed language example. आज हम speech synthesis के बारे में बात करेंगे।" }; export default function SpeechSynthesis({ voices: propVoices, language = 'english', onLanguageChange, onSynthesisComplete, className = "" }: SpeechSynthesisProps) { const [inputText, setInputText] = useState(''); const [selectedVoice, setSelectedVoice] = useState(''); const [isSynthesizing, setIsSynthesizing] = useState(false); const [isPlaying, setIsPlaying] = useState(false); const [synthesizedAudio, setSynthesizedAudio] = useState(''); const [audioElement, setAudioElement] = useState(null); const [voices, setVoices] = useState(propVoices || []); const [isLoadingVoices, setIsLoadingVoices] = useState(false); const [spectrogramData, setSpectrogramData] = useState([]); const [audioFilename, setAudioFilename] = useState(''); const [showStatsDashboard, setShowStatsDashboard] = useState(false); const [synthesizerStartTime, setSynthesizerStartTime] = useState(null); const { toast } = useToast(); useEffect(() => { // Don't set default text - let user type their own // Load voices from backend loadVoices(); }, []); const loadVoices = async () => { setIsLoadingVoices(true); try { const data = await api.fetchVoices(); const loadedVoices = data.voices.map((v: any) => ({ id: v.id, name: v.name, audioUrl: v.path })); setVoices(loadedVoices); console.log('Loaded voices:', loadedVoices); } catch (error) { console.error('Failed to load voices:', error); toast({ title: "Failed to load voices", description: "Could not fetch voices from backend", variant: "destructive" }); } finally { setIsLoadingVoices(false); } }; const handleSampleTextSelect = (type: keyof typeof sampleTexts) => { setInputText(sampleTexts[type]); }; const handleSynthesize = async () => { console.log('Synthesize clicked - Voice:', selectedVoice, 'Text:', inputText); // Debug log if (!inputText.trim()) { toast({ title: "No text provided", description: "Please enter some text to synthesize", variant: "destructive" }); return; } if (!selectedVoice) { toast({ title: "No voice selected", description: "Please select a voice for synthesis", variant: "destructive" }); return; } setIsSynthesizing(true); setSpectrogramData([]); // Reset spectrogram setSynthesizerStartTime(Date.now()); // Record synthesis start time try { // Call backend API for synthesis with language support const result = await api.synthesize(selectedVoice, inputText, language); // Get the audio file URL from backend with cache busting const audioUrl = api.getAudioUrl(result.audio_url) + `?t=${Date.now()}`; // Extract filename from audio_url (e.g., "/api/audio/synthesis_abc123.wav" -> "synthesis_abc123.wav") const filename = result.audio_url.split('/').pop() || ''; setAudioFilename(filename); // Store filename for mel-spectrogram real-time fetching // Fetch mel-spectrogram data after synthesis if (filename) { try { const spectrogramResult = await api.getSpectrogram(filename); setSpectrogramData(spectrogramResult.spectrogram); console.log('Spectrogram data loaded:', spectrogramResult); } catch (err) { console.warn('Could not load spectrogram data:', err); // Continue without spectrogram data } } // Reset audio element to force reload if (audioElement) { audioElement.pause(); audioElement.src = ''; setAudioElement(null); } setSynthesizedAudio(audioUrl); setIsPlaying(false); onSynthesisComplete?.(audioUrl); toast({ title: "Synthesis complete!", description: "Your text has been converted to speech" }); } catch (error) { console.error('Synthesis error:', error); toast({ title: "Synthesis failed", description: error instanceof Error ? error.message : "There was an error generating the speech. Please try again.", variant: "destructive" }); } finally { setIsSynthesizing(false); } }; const handlePlay = () => { if (!synthesizedAudio) return; if (audioElement) { if (isPlaying) { audioElement.pause(); setIsPlaying(false); } else { audioElement.play(); setIsPlaying(true); } } else { const audio = new Audio(synthesizedAudio); audio.onended = () => setIsPlaying(false); audio.onpause = () => setIsPlaying(false); audio.play(); setIsPlaying(true); setAudioElement(audio); } }; const handleDownload = () => { if (synthesizedAudio) { const a = document.createElement('a'); a.href = synthesizedAudio; a.download = `synthesis-${Date.now()}.wav`; document.body.appendChild(a); a.click(); document.body.removeChild(a); } }; const detectLanguage = (text: string) => { const hindiRegex = /[\u0900-\u097F]/; const hasHindi = hindiRegex.test(text); const hasEnglish = /[a-zA-Z]/.test(text); if (hasHindi && hasEnglish) return 'Mixed (English + Hindi)'; if (hasHindi) return 'Hindi'; if (hasEnglish) return 'English'; return 'Unknown'; }; return ( <>
Speech Synthesis Convert text to speech using your enrolled voices
{/* Language Selector */}
{/* Voice Selection */}
{/* Text Input */}
{detectLanguage(inputText)}