'use client' import { useState, useRef, useEffect } from 'react' import { Button } from '@/components/ui/button' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Textarea } from '@/components/ui/textarea' import { Label } from '@/components/ui/label' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { useToast } from '@/hooks/use-toast' import { Volume2, Play, Square, Download, Settings, Loader2 } from 'lucide-react' interface TtsState { isPlaying: boolean isProcessing: boolean audioBlob: Blob | null audioUrl: string | null } interface VoiceOption { id: string name: string language: string } const VOICE_OPTIONS: VoiceOption[] = [ { id: 'en-US', name: 'English (US)', language: 'en-US' }, { id: 'en-GB', name: 'English (UK)', language: 'en-GB' }, { id: 'es-ES', name: 'Spanish', language: 'es-ES' }, { id: 'fr-FR', name: 'French', language: 'fr-FR' }, { id: 'de-DE', name: 'German', language: 'de-DE' }, ] const TTS_ENDPOINT = 'https://tts41-nhdtuisbdhcvdth.siliconpin.com/tts' export function TextToSpeechClient() { const { toast } = useToast() const [ttsState, setTtsState] = useState({ isPlaying: false, isProcessing: false, audioBlob: null, audioUrl: null, }) const [text, setText] = useState('') const [selectedVoice, setSelectedVoice] = useState('en-US') const [speechRate, setSpeechRate] = useState(1.0) const [pitch, setPitch] = useState(1.0) const audioRef = useRef(null) // Clean up on unmount useEffect(() => { return () => { if (ttsState.audioUrl) { URL.revokeObjectURL(ttsState.audioUrl) } } }, [ttsState.audioUrl]) const convertTextToSpeech = async () => { if (!text.trim()) { toast({ title: 'No Text', description: 'Please enter some text to convert to speech', variant: 'destructive', }) return } setTtsState((prev) => ({ ...prev, isProcessing: true })) try { const response = await fetch(TTS_ENDPOINT, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ text: text.trim(), voice: selectedVoice, rate: speechRate, pitch: pitch, }), }) if (!response.ok) { const errorText = await response.text() throw new Error(`TTS API Error (${response.status}): ${errorText}`) } const audioBlob = await response.blob() const audioUrl = URL.createObjectURL(audioBlob) setTtsState({ isPlaying: false, isProcessing: false, audioBlob, audioUrl, }) toast({ title: 'Success', description: 'Text converted to speech successfully!', }) } catch (error) { const message = error instanceof Error ? error.message : 'TTS conversion failed' toast({ title: 'Conversion Failed', description: message, variant: 'destructive', }) setTtsState((prev) => ({ ...prev, isProcessing: false })) } } const playAudio = () => { if (ttsState.audioUrl) { if (audioRef.current) { audioRef.current.pause() } const audio = new Audio(ttsState.audioUrl) audioRef.current = audio audio.play() setTtsState((prev) => ({ ...prev, isPlaying: true })) audio.onended = () => { setTtsState((prev) => ({ ...prev, isPlaying: false })) } audio.onerror = () => { toast({ title: 'Playback Error', description: 'Failed to play audio', variant: 'destructive', }) setTtsState((prev) => ({ ...prev, isPlaying: false })) } } } const stopAudio = () => { if (audioRef.current) { audioRef.current.pause() audioRef.current = null } setTtsState((prev) => ({ ...prev, isPlaying: false })) } const downloadAudio = () => { if (ttsState.audioBlob) { const url = URL.createObjectURL(ttsState.audioBlob) const a = document.createElement('a') a.href = url a.download = `speech-${Date.now()}.wav` a.click() URL.revokeObjectURL(url) } } const hasAudio = ttsState.audioBlob !== null const canConvert = text.trim().length > 0 && !ttsState.isProcessing return ( <>

Text to Speech Converter

Convert text to natural sounding speech

{/* Text Input Section */} Enter Text