'use client' import React, { useCallback, useRef, useState } from 'react' interface AudioButtonProps { text: string lang: string isDark: boolean size?: 'sm' | 'md' | 'lg' } /** * AudioButton — plays TTS audio for a word or phrase. * * Priority: Piper TTS (Thorsten DE / Lessac EN) via backend API. * Fallback: Browser Web Speech API if Piper is unavailable. */ export function AudioButton({ text, lang, isDark, size = 'md' }: AudioButtonProps) { const [isSpeaking, setIsSpeaking] = useState(false) const audioRef = useRef(null) const speak = useCallback(async () => { // Stop if already playing if (isSpeaking) { audioRef.current?.pause() window.speechSynthesis?.cancel() setIsSpeaking(false) return } setIsSpeaking(true) // Try Piper TTS via backend API first try { const url = `/api/vocabulary/tts?text=${encodeURIComponent(text)}&lang=${lang}` const resp = await fetch(url) if (resp.ok && resp.headers.get('content-type')?.startsWith('audio')) { const blob = await resp.blob() const audioUrl = URL.createObjectURL(blob) const audio = new Audio(audioUrl) audioRef.current = audio audio.onended = () => { setIsSpeaking(false); URL.revokeObjectURL(audioUrl) } audio.onerror = () => { setIsSpeaking(false); URL.revokeObjectURL(audioUrl) } await audio.play() return } } catch { // Piper unavailable — fall through to Web Speech API } // Fallback: Browser Web Speech API if ('speechSynthesis' in window) { const utterance = new SpeechSynthesisUtterance(text) utterance.lang = lang === 'de' ? 'de-DE' : 'en-GB' utterance.rate = 0.9 const voices = window.speechSynthesis.getVoices() const preferred = voices.find((v) => v.lang.startsWith(lang === 'de' ? 'de' : 'en') && v.localService ) || voices.find((v) => v.lang.startsWith(lang === 'de' ? 'de' : 'en')) if (preferred) utterance.voice = preferred utterance.onend = () => setIsSpeaking(false) utterance.onerror = () => setIsSpeaking(false) window.speechSynthesis.speak(utterance) } else { setIsSpeaking(false) } }, [text, lang, isSpeaking]) const sizeClasses = { sm: 'w-7 h-7', md: 'w-9 h-9', lg: 'w-11 h-11' } const iconSizes = { sm: 'w-3.5 h-3.5', md: 'w-4 h-4', lg: 'w-5 h-5' } return ( ) }