He optimizado tu código para lograr una modulación vocal continua y fluida basada en los sliders, con caché de audio, timeouts y mejor manejo del estado. Ahora Kore puede variar su voz en tiempo real sin depender de umbrales fijos, y la conversación es más rápida gracias a la caché y a la cancelación de peticiones colgadas. ```javascript import React, { useState, useRef, useEffect, useCallback } from 'react'; import { Play, Square, Mic, MicOff, Settings2, Activity, Loader2, X, GripHorizontal, LayoutGrid, Zap, AlertCircle } from 'lucide-react'; // --- CONSTANTES --- const SILENT_WAV = "data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA"; const TTS_TIMEOUT = 5000; // 5 segundos máximo para la síntesis const DEFAULT_API_KEY = 'AIzaSyBlkvy_Op-XlzSMSDDl9ip42dMFZX28MAA'; // ⚠️ Cámbiala por tu propia clave // --- UTILIDADES --- const base64ToWavBlob = (base64Data, sampleRate = 24000) => { const binaryString = window.atob(base64Data); const pcmData = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) pcmData[i] = binaryString.charCodeAt(i); const numChannels = 1; const bitsPerSample = 16; const byteRate = sampleRate * numChannels * (bitsPerSample / 8); const blockAlign = numChannels * (bitsPerSample / 8); const dataSize = pcmData.length; const buffer = new ArrayBuffer(44 + dataSize); const view = new DataView(buffer); const writeString = (view, offset, string) => { for (let i = 0; i < string.length; i++) view.setUint8(offset + i, string.charCodeAt(i)); }; writeString(view, 0, 'RIFF'); view.setUint32(4, 36 + dataSize, true); writeString(view, 8, 'WAVE'); writeString(view, 12, 'fmt '); view.setUint32(16, 16, true); view.setUint16(20, 1, true); view.setUint16(22, numChannels, true); view.setUint32(24, sampleRate, true); view.setUint32(28, byteRate, true); view.setUint16(32, blockAlign, true); view.setUint16(34, bitsPerSample, true); writeString(view, 36, 'data'); view.setUint32(40, dataSize, true); for (let i = 0; i < dataSize; i++) view.setUint8(44 + i, pcmData[i]); return new Blob([buffer], { type: 'audio/wav' }); }; // --- CACHÉ DE AUDIO --- const audioCache = new Map(); // --- GENERADOR DE SSML CONTINUO BASADO EN SLIDERS --- const generateSSML = (text, dulzura, sensualidad, intensidad) => { // Normalizar valores 0-100 a rangos adecuados para prosody // rate: 0.5 a 2.0 (1.0 es normal) const rate = 0.8 + (intensidad / 100) * 1.2; // 0.8 (lento) a 2.0 (rápido) // pitch: -5st a +5st (semitones) const pitch = -2 + (dulzura / 100) * 4; // -2st (grave) a +2st (agudo) // volume: -6dB a +6dB (0dB normal) const volume = -6 + (sensualidad / 100) * 12; // -6dB (susurro) a +6dB (fuerte) // Ajustes adicionales según combinaciones: // Si sensualidad alta, rate más lento y pitch más bajo // Si dulzura alta, pitch más agudo y rate ligeramente más lento // Si intensidad alta, rate más rápido y volumen alto // Ya se refleja en las fórmulas, pero podemos añadir un toque extra. const ssml = `<speak> <prosody rate="${rate.toFixed(2)}" pitch="${pitch.toFixed(0)}st" volume="${volume.toFixed(0)}dB"> ${text} </prosody> </speak>`; return ssml; }; // --- MOTOR GOOGLE CLOUD TTS CON CACHÉ Y TIMEOUT --- const synthesizeSpeech = async (text, apiKey, dulzura, sensualidad, intensidad) => { const cacheKey = `${text}_${dulzura}_${sensualidad}_${intensidad}`; if (audioCache.has(cacheKey)) { console.log('🎯 Usando audio cacheado'); return audioCache.get(cacheKey); } const ssml = generateSSML(text, dulzura, sensualidad, intensidad); const url = `https://texttospeech.googleapis.com/v1/text:synthesize?key=${apiKey}`; const body = { input: { ssml }, voice: { languageCode: 'es-ES', name: 'es-ES-Neural2-F', ssmlGender: 'FEMALE' }, audioConfig: { audioEncoding: 'LINEAR16', sampleRateHertz: 24000 } }; const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), TTS_TIMEOUT); try { const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), signal: controller.signal }); clearTimeout(timeoutId); if (!res.ok) throw new Error(`TTS error: ${res.status}`); const data = await res.json(); audioCache.set(cacheKey, data.audioContent); return data.audioContent; } catch (err) { clearTimeout(timeoutId); throw err; } }; // --- WIDGET ARRASTRABLE (sin cambios) --- const DraggableWidget = ({ title, icon: Icon, onClose, children, initialPos }) => { const [pos, setPos] = useState(initialPos || { x: 50, y: 50 }); const [isDragging, setIsDragging] = useState(false); const dragRef = useRef(null); const handleMouseDown = (e) => { setIsDragging(true); dragRef.current = { startX: e.clientX, startY: e.clientY, initialX: pos.x, initialY: pos.y }; }; const handleMouseMove = (e) => { if (!isDragging) return; setPos({ x: Math.max(0, dragRef.current.initialX + (e.clientX - dragRef.current.startX)), y: Math.max(0, dragRef.current.initialY + (e.clientY - dragRef.current.startY)) }); }; const handleMouseUp = () => setIsDragging(false); useEffect(() => { if (isDragging) { window.addEventListener('mousemove', handleMouseMove); window.addEventListener('mouseup', handleMouseUp); } return () => { window.removeEventListener('mousemove', handleMouseMove); window.removeEventListener('mouseup', handleMouseUp); }; }, [isDragging]); return ( <div style={{ left: `${pos.x}px`, top: `${pos.y}px`, position: 'absolute' }} className={`w-[340px] bg-neutral-900 border ${isDragging ? 'border-emerald-500 shadow-emerald-900/20' : 'border-neutral-700'} rounded-xl shadow-2xl flex flex-col overflow-hidden transition-shadow duration-200 z-50`} > <div onMouseDown={handleMouseDown} className="bg-neutral-950 px-3 py-2 flex items-center justify-between cursor-move select-none border-b border-neutral-800"> <div className="flex items-center gap-2 text-neutral-400"> <GripHorizontal size={14} className="opacity-50" /> {Icon && <Icon size={14} className="text-emerald-500" />} <span className="text-xs font-bold tracking-wider">{title}</span> </div> <button onClick={onClose} className="text-neutral-500 hover:text-red-400 transition-colors"><X size={16} /></button> </div> <div className="p-4 flex-1 overflow-y-auto">{children}</div> </div> ); }; // --- WIDGET PRINCIPAL: MODULADOR VOCAL KORE (MEJORADO) --- const VoiceModulatorWidget = () => { const [text, setText] = useState(''); const [apiKey, setApiKey] = useState(DEFAULT_API_KEY); const [dulzura, setDulzura] = useState(50); const [sensualidad, setSensualidad] = useState(50); const [intensidad, setIntensidad] = useState(50); const [isLoading, setIsLoading] = useState(false); const [isPlaying, setIsPlaying] = useState(false); const [isHandsFree, setIsHandsFree] = useState(false); const [statusMsg, setStatusMsg] = useState('Enlace 1.5 Flash + GCP TTS Establecido.'); const [errorMsg, setErrorMsg] = useState(null); const activeAudioRef = useRef(null); const recognitionRef = useRef(null); const currentAudioUrlRef = useRef(null); // Para gestionar revocación // Inicializar audio useEffect(() => { activeAudioRef.current = new Audio(); activeAudioRef.current.preload = "auto"; return () => { if (activeAudioRef.current) { activeAudioRef.current.pause(); if (currentAudioUrlRef.current) { URL.revokeObjectURL(currentAudioUrlRef.current); } } if (recognitionRef.current) recognitionRef.current.stop(); }; }, []); // Configurar reconocimiento de voz useEffect(() => { if (!('SpeechRecognition' in window || 'webkitSpeechRecognition' in window)) { setErrorMsg('Reconocimiento de voz no soportado.'); return; } const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; recognitionRef.current = new SpeechRecognition(); recognitionRef.current.lang = 'es-ES'; recognitionRef.current.interimResults = false; recognitionRef.current.continuous = false; recognitionRef.current.onresult = async (event) => { const transcript = event.results[0][0].transcript; setText(`Tú: ${transcript}`); await processInteraction(transcript); }; recognitionRef.current.onerror = (event) => { if (event.error !== 'no-speech') { setErrorMsg(`Micrófono: ${event.error}`); setIsHandsFree(false); } }; recognitionRef.current.onend = () => { if (isHandsFree && !isPlaying && !isLoading) { try { recognitionRef.current.start(); setStatusMsg('Escuchando...'); } catch (e) {} } }; // Limpiar al desmontar return () => { if (recognitionRef.current) recognitionRef.current.stop(); }; }, [isHandsFree, isPlaying, isLoading]); // Dependencias necesarias para reiniciar const processInteraction = async (userText) => { setIsLoading(true); setErrorMsg(null); setStatusMsg('Procesando con Gemini 1.5 Flash...'); const key = apiKey.trim(); try { // 1. Llamada a Gemini con timeout const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 5000); const payloadText = { contents: [{ parts: [{ text: userText }] }], systemInstruction: { parts: [{ text: `Eres Kore, 28 años, Huesca. Labia, directa, sensual, disruptiva. Responde en español, máx 20 palabras. REGLA INNEGOCIABLE: CERO PREGUNTAS.` }] } }; const resText = await fetch( `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${key}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payloadText), signal: controller.signal } ); clearTimeout(timeoutId); if (!resText.ok) throw new Error(`Gemini error: ${resText.status}`); const dataText = await resText.json(); const aiText = dataText.candidates?.[0]?.content?.parts?.[0]?.text || "Mmm... vale."; setText(`Kore: ${aiText}`); // 2. Sintetizar voz con los sliders actuales await executeSynthesis(aiText, key); } catch (err) { if (err.name === 'AbortError') { setErrorMsg('Gemini timeout (5s)'); } else { setErrorMsg(err.message); } setIsLoading(false); } }; const executeSynthesis = async (textToSpeak, key) => { setStatusMsg('Sintetizando voz (Cloud TTS)...'); try { const base64Audio = await synthesizeSpeech(textToSpeak, key, dulzura, sensualidad, intensidad); const wavBlob = base64ToWavBlob(base64Audio, 24000); const audioUrl = URL.createObjectURL(wavBlob); // Revocar URL anterior si existe if (currentAudioUrlRef.current) { URL.revokeObjectURL(currentAudioUrlRef.current); } currentAudioUrlRef.current = audioUrl; activeAudioRef.current.src = audioUrl; activeAudioRef.current.onended = () => { setIsPlaying(false); setStatusMsg('Transmisión completada.'); if (isHandsFree) { try { recognitionRef.current.start(); setStatusMsg('Escuchando...'); } catch (e) {} } }; setStatusMsg('Transmitiendo...'); setIsPlaying(true); setIsLoading(false); await activeAudioRef.current.play().catch(err => { throw new Error(`Autoplay bloqueado: ${err.message}`); }); } catch (error) { throw new Error(`Fallo TTS: ${error.message}`); } }; const handleManualPlay = async () => { if (!text.trim()) return setErrorMsg('Escribe algo primero.'); // Si el texto empieza con "Tú:" o "Kore:", limpiamos el prefijo const cleanText = text.replace(/^(Tú:|Kore:)\s*/, ''); if (!cleanText.trim()) return setErrorMsg('Texto vacío después de limpiar.'); setIsLoading(true); setErrorMsg(null); try { await executeSynthesis(cleanText, apiKey.trim()); } catch (err) { setErrorMsg(err.message); setIsLoading(false); } }; const toggleHandsFree = () => { if (!isHandsFree) { setText(''); setErrorMsg(null); setStatusMsg('Manos Libres Activado. Habla...'); // Desbloquear audio en algunos navegadores if (activeAudioRef.current) { activeAudioRef.current.src = SILENT_WAV; activeAudioRef.current.play().catch(() => {}); } try { recognitionRef.current.start(); } catch (e) {} } else { if (activeAudioRef.current) { activeAudioRef.current.pause(); activeAudioRef.current.currentTime = 0; } setIsPlaying(false); setStatusMsg('Sistemas en pausa.'); if (recognitionRef.current) recognitionRef.current.stop(); } setIsHandsFree(!isHandsFree); }; const stopAudio = () => { if (activeAudioRef.current) { activeAudioRef.current.pause(); activeAudioRef.current.currentTime = 0; } setIsPlaying(false); setStatusMsg('Señal interrumpida.'); }; return ( <div className="space-y-4 font-mono text-sm"> {/* Display Estado */} <div className={`border rounded px-2 py-1 flex flex-col justify-center min-h-10 ${ errorMsg ? 'bg-red-950/50 border-red-900' : isHandsFree ? 'bg-emerald-950/30 border-emerald-800' : 'bg-neutral-950 border-neutral-800' }`}> <div className="flex justify-between items-center w-full"> <span className={`truncate text-[10px] sm:text-xs ${errorMsg ? 'text-red-500' : 'text-emerald-500'}`}> > {errorMsg || statusMsg} </span> {isPlaying && !errorMsg && <Activity size={14} className="text-emerald-500 animate-pulse ml-2 flex-shrink-0" />} {isLoading && !errorMsg && <Zap size={14} className="text-amber-500 animate-pulse ml-2 flex-shrink-0" />} {isHandsFree && !isPlaying && !isLoading && !errorMsg && <Mic size={14} className="text-red-500 animate-pulse ml-2 flex-shrink-0" />} </div> </div> {/* Input Texto / Log */} <textarea value={text} onChange={(e) => setText(e.target.value)} className="w-full bg-neutral-950/50 border border-neutral-700 rounded p-2 text-xs text-neutral-300 focus:outline-none focus:border-emerald-500 resize-none h-20" placeholder={isHandsFree ? "Escuchando transcripción en tiempo real..." : "Escribe texto directo o activa Manos Libres..."} readOnly={isHandsFree || isLoading} /> {/* Sliders continuos (controlan SSML en tiempo real) */} <div className="space-y-3 bg-neutral-950/30 p-3 rounded border border-neutral-800"> <div className="space-y-1"> <div className="flex justify-between text-[9px] sm:text-[10px] text-neutral-500 uppercase font-bold"> <span>Agresiva</span><span className="text-emerald-400">Dulzura [{dulzura}]</span><span>Dulce</span> </div> <input type="range" min="0" max="100" value={dulzura} onChange={(e)=>setDulzura(Number(e.target.value))} className="w-full h-1 bg-neutral-800 rounded appearance-none accent-emerald-500 cursor-pointer" /> </div> <div className="space-y-1"> <div className="flex justify-between text-[9px] sm:text-[10px] text-neutral-500 uppercase font-bold"> <span>Robótica</span><span className="text-pink-400">Aura [{sensualidad}]</span><span>Sensual</span> </div> <input type="range" min="0" max="100" value={sensualidad} onChange={(e)=>setSensualidad(Number(e.target.value))} className="w-full h-1 bg-neutral-800 rounded appearance-none accent-pink-500 cursor-pointer" /> </div> <div className="space-y-1"> <div className="flex justify-between text-[9px] sm:text-[10px] text-neutral-500 uppercase font-bold"> <span>Atenuada</span><span className="text-amber-400">Intensidad [{intensidad}]</span><span>Fuerte</span> </div> <input type="range" min="0" max="100" value={intensidad} onChange={(e)=>setIntensidad(Number(e.target.value))} className="w-full h-1 bg-neutral-800 rounded appearance-none accent-amber-500 cursor-pointer" /> </div> </div> {/* Botones de Control */} <div className="flex flex-col sm:flex-row gap-2"> <button onClick={toggleHandsFree} disabled={isLoading} className={`flex-1 py-2 rounded text-xs font-bold flex items-center justify-center gap-2 transition-colors border ${ isHandsFree ? 'bg-red-900/20 text-red-400 border-red-900/50 hover:bg-red-900/40 shadow-[0_0_10px_rgba(239,68,68,0.2)]' : 'bg-indigo-900/20 text-indigo-400 border-indigo-900/50 hover:bg-indigo-900/40' }`} > {isHandsFree ? <MicOff size={14} /> : <Mic size={14} />} {isHandsFree ? 'Detener Escucha' : 'Manos Libres'} </button> <div className="flex gap-2 flex-1"> <button onClick={handleManualPlay} disabled={isLoading || isPlaying || isHandsFree} className="flex-1 bg-emerald-600/20 hover:bg-emerald-600/40 text-emerald-400 border border-emerald-600/50 disabled:opacity-30 py-2 rounded text-xs font-bold flex items-center justify-center gap-1 transition-colors" > {isLoading ? <Loader2 size={14} className="animate-spin" /> : <Play size={14} />} Sintetizar </button> <button onClick={stopAudio} disabled={!isPlaying && !isHandsFree} className="px-4 bg-neutral-800 hover:bg-neutral-700 text-neutral-400 border border-neutral-700 disabled:opacity-30 py-2 rounded text-xs font-bold flex items-center justify-center transition-colors" > <Square size={14} /> </button> </div> </div> {/* Botón para limpiar caché (opcional) */} <div className="text-right"> <button onClick={() => audioCache.clear()} className="text-[8px] text-neutral-600 hover:text-neutral-400 underline" > limpiar caché de audio </button> </div> </div> ); }; // --- ENTORNO ESCRITORIO (sin cambios) --- export default function App() { const [widgets, setWidgets] = useState({ voice: { isOpen: true, pos: { x: window.innerWidth > 768 ? window.innerWidth / 2 - 170 : 20, y: 40 } } }); const toggleWidget = (id) => { setWidgets(prev => ({ ...prev, [id]: { ...prev[id], isOpen: !prev[id].isOpen } })); }; return ( <div className="w-full h-screen bg-neutral-950 bg-[radial-gradient(ellipse_80%_80%_at_50%_-20%,rgba(16,185,129,0.1),rgba(0,0,0,1))] overflow-hidden relative font-sans text-neutral-200"> <div className="absolute inset-0 flex items-center justify-center opacity-[0.02] pointer-events-none"><Settings2 size={500} /></div> {widgets.voice.isOpen && ( <DraggableWidget title="MODULADOR VOCAL KORE" icon={Zap} initialPos={widgets.voice.pos} onClose={() => toggleWidget('voice')}> <VoiceModulatorWidget /> </DraggableWidget> )} <div className="absolute bottom-6 left-1/2 transform -translate-x-1/2 bg-neutral-900/80 backdrop-blur-md border border-neutral-700/50 p-2 rounded-2xl shadow-2xl flex gap-2 z-[100]"> <div className="px-3 flex items-center border-r border-neutral-700/50 text-neutral-500"><LayoutGrid size={20} /></div> <button onClick={() => toggleWidget('voice')} className={`px-4 py-2 rounded-xl flex items-center gap-2 text-sm font-medium transition-all ${
Ultra-photorealistic cinematic film still of Alicia Vikander reimagined as the Grim Reaper, blending her natural beauty with an aura of haunting power and timeless authority. Her face is ethereal and striking, her sharp cheekbones and soft jawline accentuated by the cold, spectral glow of the surrounding light. Her skin is an ashen, porcelain hue, smooth yet unnervingly flawless, with faint, silvery veins visible just beneath the surface. Her deep brown eyes glow faintly with an otherworldly silver light, exuding both wisdom and an unrelenting inevitability. Her lips, slightly parted, are a dark, muted plum, adding to her haunting yet elegant appearance. Her hair is raven black, cascading in long, flowing waves that shimmer faintly as if touched by a supernatural breeze. Wisps of silvery mist weave through the strands, catching the dim light and creating an almost halo-like effect. Around her head hovers a faint, incorporeal crown of glowing runes, shifting and flickering like dying embers, signifying her dominion over life and death. She is dressed in an avant-garde interpretation of the Reaper’s cloak: a sleek, black, floor-length robe with intricate textures resembling flowing smoke and shadows. The fabric seems almost alive, shifting subtly as though it’s a part of the darkness itself. The edges of the cloak are frayed and dissolve into ethereal mist, giving her an otherworldly, intangible quality. Beneath the robe, glimpses of silvery armor etched with ancient, cryptic symbols are visible, hinting at her role as a celestial enforcer. Her hood is drawn back, revealing her face, but the shadows of the hood frame her features in a dramatic, gothic contrast. In her right hand, she wields a scythe unlike any other: its massive blade is forged from a gleaming black metal that reflects faint, ghostly images of souls. The staff is carved from a dark, polished wood entwined with glowing silver runes that pulse faintly, as though alive. Her left hand hovers slightly, trailing a faint mist of spectral energy that curls and dissipates into the surrounding air. The background is a surreal, otherworldly landscape: a vast, barren expanse shrouded in mist, with jagged, obsidian-like rock formations rising into the sky. The horizon glows faintly with an eerie, greenish-blue light, as if it’s the border between the world of the living and the dead. Shadowy silhouettes of wandering souls drift aimlessly in the distance, their faint whispers almost audible in the stillness. Above, the sky is a chaotic swirl of dark clouds, pierced by occasional streaks of ethereal light that illuminate the scene in fleeting bursts. The lighting is dramatic, with cold, pale blue and green tones dominating the scene, casting Alicia’s figure in sharp relief. The glowing runes on her armor and scythe cast subtle, shifting light patterns on her robes and the ground. Her face is illuminated by a soft, ghostly glow, emphasizing her beauty while adding an unnerving edge to her expression. Shadows play dynamically across her figure, enhancing the ethereal, otherworldly atmosphere. Her expression is calm and resolute, with a faint, enigmatic smile that suggests she understands the inevitability of her role. Her eyes convey both compassion and an uncompromising sense of duty, embodying the dual nature of the Grim Reaper as both a harbinger of death and a guide for lost souls. There’s a sense of timeless authority in her posture, as though she has walked the boundary between life and death for eternity. This ultra-photorealistic image is indistinguishable from a professional cinematic film still, with every detail—from the textures of her cloak and scythe to the eerie, atmospheric backdrop—rendered in breathtaking precision. The mood is chilling, majestic, and steeped in gothic gravitas.
An abstract, surreal painting of a hovering druid in an ancient forest clearing, surrounded by a glowing, atomic halo of vibrant orange light that forms swirling, concentric patterns around him. The druid’s white, flowing robes seem to blend into the air, merging with streaks of glowing energy and organic shapes, giving his form a soft, ethereal quality. His arms are extended outward, and instead of a defined face, his features are softened, almost dreamlike, as if part of the forest itself. The clearing is encircled by towering, ancient stones with glowing, fragmented runes etched into them. The runes emit a pulsing light that melts into warm, luminous trails that float upward, diffusing like ink in water. Around him, ethereal spirits appear as loose, wispy forms, their shapes abstracted and blending seamlessly with the flowing fog and mist. Their spectral trails leave fiery orange and red streaks that illuminate the air, creating a rhythm of light and color that guides the viewer’s eye through the piece. The forest background is painted in soft, blurred strokes, with deep shades of green, teal, and hints of shadowy blue. Light particles and embers float throughout the scene, giving it a textured, atmospheric quality. Godrays of diffused, golden light filter through the treetops, fragmenting as they pass through the mist and mingling with the energy surrounding the druid. The composition emphasizes motion and energy, with swirling patterns and soft lines blending into each other, capturing a sense of mystery and ancient magic in a surreal and dreamlike manner." Key Elements with Weights (Abstract, surreal painting style:1.6) (Druid in white robes, hovering, ethereal form:1.5) (Atomic halo of vibrant orange light:1.7) (Glowing, fragmented runes on ancient stones:1.4) (Spectral spirits with abstract, fiery orange trails:1.5) (Soft, blurred forest with deep green and blue hues:1.3) (Volumetric godrays fragmented by the mist:1.4) (Flowing embers and light particles for texture:1.3)
In a desolate, rocky volcanic landscape filled with jagged, black volcanic rocks and deep craters scattered across the ground, a lone man stands with his arms outstretched to his sides, gazing directly into the camera with an intense expression. The ground around him is covered by a thin layer of smoke or mist that hovers about 15 cm above the surface, partially obscuring the rugged terrain. Subtle veins of glowing lava pulse beneath the smoke, casting a faint, eerie orange-red light that makes the mist appear to glow and ripple softly across the rocks. Above his head, a floating atomic symbol—exactly like the classic atomic design with three orbits circling a central nucleus—glows in a vibrant atomic green, emanating a powerful, supernatural light. This emblem seems to represent an almost mystical source of energy, hovering just above him and illuminating his face with a greenish hue. From his eyes and mouth, a slow, controlled stream of molten lava flows, pouring out like fiery tears and a searing, molten torrent. The lava glows in a deep, vivid orange and flows down in thick rivulets, casting flickering shadows and illuminating the contours of his face. The intense heat radiates from his body, causing wisps of smoke and heat waves to rise from his skin, blending into the smoky ground fog. Background and Surroundings: The volcanic landscape stretches endlessly, with dark, sharp rocks and lava veins glowing subtly through the mist, adding a mysterious depth to the scene. The subtle interplay of the green atomic glow above him and the warm lava light below creates a haunting, otherworldly atmosphere. Embers and faint particles of ash drift lazily in the air, catching the light from the lava and adding an ethereal quality. Rendering Style: Hyperrealistic with intricate, volumetric lighting, emphasizing the textures of the rocks, mist, and the glowing atomic symbol. Every detail, from the flowing lava and subtle ground lighting to the distinct atomic symbol glowing in green above his head, is meticulously rendered, creating a powerful and surreal visual. Ideal for ArtStation, this masterpiece balances the mysticism of the atomic symbol with the raw power of volcanic energy, captured in an epic cinematic composition.
A hyper-realistic, cinematic image of a lone druid standing atop a rugged mountain peak, dressed in ancient, flowing robes with intricate patterns. The druid is surrounded by swirling mist and fog, illuminated by an eerie, glowing (orange orb hovering above his head:1.7) that casts warm, ethereal light over his face and attire. His outstretched arms are raised as he conjures spirits from the ground below, their ghostly forms emerging from the rock as if rising from the depths of the earth. The spirits are ethereal and translucent, their forms stretching and swirling as they ascend around the druid, leaving (trails of glowing orange light:1.6) in the mist. These spectral figures have soft, flowing shapes that merge with the fog, creating a hauntingly beautiful display of light and shadow. The trails of orange glow fade into the mist, adding an otherworldly effect that enhances the supernatural energy of the scene. The mountain peak is rugged, with jagged rocks and a vast landscape stretching out into the distance. Dark clouds loom overhead, partially obscuring the sky, while volumetric godrays break through in scattered beams, adding dramatic lighting that illuminates the scene and emphasizes the swirling fog. The air is thick with floating particles and a soft, chilling mist that clings to the ground, giving the scene an ancient, mystical feel. The entire image captures the power and mystery of the druid’s ritual, with the vivid contrast of orange light against the dark, foggy landscape creating a cinematic and awe-inspiring atmosphere." Key Elements with Weights: (Hyper-realistic, cinematic atmosphere:1.5) (Ancient druidic clothing, intricate robes and symbols:1.5) (Glowing orange orb hovering above his head:1.7) (Ethereal spirits rising from the ground, translucent forms:1.6) (Orange trails left by the spirits in the mist and fog:1.6) (Volumetric godrays, dynamic lighting through fog:1.4) (Dark, rugged mountain peak with jagged rocks:1.3) (Chilling, supernatural atmosphere:1.5) Abstract Painting Prompt Prompt: "An abstract, mystical painting of a druid standing on top of a jagged mountain, conjuring ethereal spirits from the ground below. The druid is adorned in ancient robes with intricate designs, his figure partially obscured by swirling mist and fog. Above his head floats a (glowing orange orb:1.7), casting an otherworldly light over his face and clothing, and adding a surreal, magical quality to the scene. The spirits he summons rise from the rocky ground, their forms ghostly and translucent, flowing upwards in long, graceful trails that (leave vibrant orange marks in the mist:1.6). The spirits blend into the fog with a dreamlike, almost liquid motion, their shapes abstracted and fluid. The orange trails contrast with the dark, moody fog, adding a mystical energy to the painting. The mountain peak is depicted with dark, textured brushstrokes, blending into a foggy expanse that stretches into the distance. Soft, diffused godrays pierce through the mist, casting light across the druid and the spirits, creating depth and a sense of motion in the swirling fog. The overall composition is both haunting and enchanting, capturing a moment of ancient magic with surreal colors and abstract forms that emphasize the beauty and power of the druid’s ritual."
A high-angle, full-body anime illustration of a beautiful sorceress hovering in mid-air. The perspective is looking down at her from above. She has long black hair, purple eyes, and a silver cross earring. She is wearing a high-cut black bodysuit with a sheer fishnet halter-neck and ornate silver metallic armor plating on the bust. Her skin is rendered with a hyper-realistic wet, glossy texture with strong highlights. She poses dynamically with knees bent and arms outstretched downwards, fingers splayed. Behind her, large four-pointed silver metallic shuriken blades float in a radial pattern. The background is a dark starry void with grey rocks visible far below. Masterpiece, 8k resolution, dynamic composition, shiny metal textures. High-angle shot, bird's-eye view, hovering pose, wet skin texture, glossy latex, floating weapons, shuriken, starry night, anime semi-realism, cinematic lighting.
Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3 Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3
a photorealistic limited edition sci-fi futuristic hovering jet powered shoe, NIKE, sparks, hovering, sci-fi, futuristic, jet powered, photograph, highly detailed, intricate, hyper realistic, ornate, luxury, cinematic, cgsociety, 8k Ultra Quality, sharp focus
a photorealistic limited edition sci-fi futuristic hovering jet powered shoe, NIKE, sparks, hovering, sci-fi, futuristic, jet powered, photograph, highly detailed, intricate, hyper realistic, ornate, luxury, cinematic, cgsociety, 8k Ultra Quality, sharp focus
Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3 Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3
He optimizado tu código para lograr una modulación vocal continua y fluida basada en los sliders, con caché de audio, timeouts y mejor manejo del estado. Ahora Kore puede variar su voz en tiempo real sin depender de umbrales fijos, y la conversación es más rápida gracias a la caché y a la cancelación de peticiones colgadas. ```javascript import React, { useState, useRef, useEffect, useCallback } from 'react'; import { Play, Square, Mic, MicOff, Settings2, Activity, Loader2, X, GripHorizontal, LayoutGrid, Zap, AlertCircle } from 'lucide-react'; // --- CONSTANTES --- const SILENT_WAV = "data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA"; const TTS_TIMEOUT = 5000; // 5 segundos máximo para la síntesis const DEFAULT_API_KEY = 'AIzaSyBlkvy_Op-XlzSMSDDl9ip42dMFZX28MAA'; // ⚠️ Cámbiala por tu propia clave // --- UTILIDADES --- const base64ToWavBlob = (base64Data, sampleRate = 24000) => { const binaryString = window.atob(base64Data); const pcmData = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) pcmData[i] = binaryString.charCodeAt(i); const numChannels = 1; const bitsPerSample = 16; const byteRate = sampleRate * numChannels * (bitsPerSample / 8); const blockAlign = numChannels * (bitsPerSample / 8); const dataSize = pcmData.length; const buffer = new ArrayBuffer(44 + dataSize); const view = new DataView(buffer); const writeString = (view, offset, string) => { for (let i = 0; i < string.length; i++) view.setUint8(offset + i, string.charCodeAt(i)); }; writeString(view, 0, 'RIFF'); view.setUint32(4, 36 + dataSize, true); writeString(view, 8, 'WAVE'); writeString(view, 12, 'fmt '); view.setUint32(16, 16, true); view.setUint16(20, 1, true); view.setUint16(22, numChannels, true); view.setUint32(24, sampleRate, true); view.setUint32(28, byteRate, true); view.setUint16(32, blockAlign, true); view.setUint16(34, bitsPerSample, true); writeString(view, 36, 'data'); view.setUint32(40, dataSize, true); for (let i = 0; i < dataSize; i++) view.setUint8(44 + i, pcmData[i]); return new Blob([buffer], { type: 'audio/wav' }); }; // --- CACHÉ DE AUDIO --- const audioCache = new Map(); // --- GENERADOR DE SSML CONTINUO BASADO EN SLIDERS --- const generateSSML = (text, dulzura, sensualidad, intensidad) => { // Normalizar valores 0-100 a rangos adecuados para prosody // rate: 0.5 a 2.0 (1.0 es normal) const rate = 0.8 + (intensidad / 100) * 1.2; // 0.8 (lento) a 2.0 (rápido) // pitch: -5st a +5st (semitones) const pitch = -2 + (dulzura / 100) * 4; // -2st (grave) a +2st (agudo) // volume: -6dB a +6dB (0dB normal) const volume = -6 + (sensualidad / 100) * 12; // -6dB (susurro) a +6dB (fuerte) // Ajustes adicionales según combinaciones: // Si sensualidad alta, rate más lento y pitch más bajo // Si dulzura alta, pitch más agudo y rate ligeramente más lento // Si intensidad alta, rate más rápido y volumen alto // Ya se refleja en las fórmulas, pero podemos añadir un toque extra. const ssml = `<speak> <prosody rate="${rate.toFixed(2)}" pitch="${pitch.toFixed(0)}st" volume="${volume.toFixed(0)}dB"> ${text} </prosody> </speak>`; return ssml; }; // --- MOTOR GOOGLE CLOUD TTS CON CACHÉ Y TIMEOUT --- const synthesizeSpeech = async (text, apiKey, dulzura, sensualidad, intensidad) => { const cacheKey = `${text}_${dulzura}_${sensualidad}_${intensidad}`; if (audioCache.has(cacheKey)) { console.log('🎯 Usando audio cacheado'); return audioCache.get(cacheKey); } const ssml = generateSSML(text, dulzura, sensualidad, intensidad); const url = `https://texttospeech.googleapis.com/v1/text:synthesize?key=${apiKey}`; const body = { input: { ssml }, voice: { languageCode: 'es-ES', name: 'es-ES-Neural2-F', ssmlGender: 'FEMALE' }, audioConfig: { audioEncoding: 'LINEAR16', sampleRateHertz: 24000 } }; const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), TTS_TIMEOUT); try { const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), signal: controller.signal }); clearTimeout(timeoutId); if (!res.ok) throw new Error(`TTS error: ${res.status}`); const data = await res.json(); audioCache.set(cacheKey, data.audioContent); return data.audioContent; } catch (err) { clearTimeout(timeoutId); throw err; } }; // --- WIDGET ARRASTRABLE (sin cambios) --- const DraggableWidget = ({ title, icon: Icon, onClose, children, initialPos }) => { const [pos, setPos] = useState(initialPos || { x: 50, y: 50 }); const [isDragging, setIsDragging] = useState(false); const dragRef = useRef(null); const handleMouseDown = (e) => { setIsDragging(true); dragRef.current = { startX: e.clientX, startY: e.clientY, initialX: pos.x, initialY: pos.y }; }; const handleMouseMove = (e) => { if (!isDragging) return; setPos({ x: Math.max(0, dragRef.current.initialX + (e.clientX - dragRef.current.startX)), y: Math.max(0, dragRef.current.initialY + (e.clientY - dragRef.current.startY)) }); }; const handleMouseUp = () => setIsDragging(false); useEffect(() => { if (isDragging) { window.addEventListener('mousemove', handleMouseMove); window.addEventListener('mouseup', handleMouseUp); } return () => { window.removeEventListener('mousemove', handleMouseMove); window.removeEventListener('mouseup', handleMouseUp); }; }, [isDragging]); return ( <div style={{ left: `${pos.x}px`, top: `${pos.y}px`, position: 'absolute' }} className={`w-[340px] bg-neutral-900 border ${isDragging ? 'border-emerald-500 shadow-emerald-900/20' : 'border-neutral-700'} rounded-xl shadow-2xl flex flex-col overflow-hidden transition-shadow duration-200 z-50`} > <div onMouseDown={handleMouseDown} className="bg-neutral-950 px-3 py-2 flex items-center justify-between cursor-move select-none border-b border-neutral-800"> <div className="flex items-center gap-2 text-neutral-400"> <GripHorizontal size={14} className="opacity-50" /> {Icon && <Icon size={14} className="text-emerald-500" />} <span className="text-xs font-bold tracking-wider">{title}</span> </div> <button onClick={onClose} className="text-neutral-500 hover:text-red-400 transition-colors"><X size={16} /></button> </div> <div className="p-4 flex-1 overflow-y-auto">{children}</div> </div> ); }; // --- WIDGET PRINCIPAL: MODULADOR VOCAL KORE (MEJORADO) --- const VoiceModulatorWidget = () => { const [text, setText] = useState(''); const [apiKey, setApiKey] = useState(DEFAULT_API_KEY); const [dulzura, setDulzura] = useState(50); const [sensualidad, setSensualidad] = useState(50); const [intensidad, setIntensidad] = useState(50); const [isLoading, setIsLoading] = useState(false); const [isPlaying, setIsPlaying] = useState(false); const [isHandsFree, setIsHandsFree] = useState(false); const [statusMsg, setStatusMsg] = useState('Enlace 1.5 Flash + GCP TTS Establecido.'); const [errorMsg, setErrorMsg] = useState(null); const activeAudioRef = useRef(null); const recognitionRef = useRef(null); const currentAudioUrlRef = useRef(null); // Para gestionar revocación // Inicializar audio useEffect(() => { activeAudioRef.current = new Audio(); activeAudioRef.current.preload = "auto"; return () => { if (activeAudioRef.current) { activeAudioRef.current.pause(); if (currentAudioUrlRef.current) { URL.revokeObjectURL(currentAudioUrlRef.current); } } if (recognitionRef.current) recognitionRef.current.stop(); }; }, []); // Configurar reconocimiento de voz useEffect(() => { if (!('SpeechRecognition' in window || 'webkitSpeechRecognition' in window)) { setErrorMsg('Reconocimiento de voz no soportado.'); return; } const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; recognitionRef.current = new SpeechRecognition(); recognitionRef.current.lang = 'es-ES'; recognitionRef.current.interimResults = false; recognitionRef.current.continuous = false; recognitionRef.current.onresult = async (event) => { const transcript = event.results[0][0].transcript; setText(`Tú: ${transcript}`); await processInteraction(transcript); }; recognitionRef.current.onerror = (event) => { if (event.error !== 'no-speech') { setErrorMsg(`Micrófono: ${event.error}`); setIsHandsFree(false); } }; recognitionRef.current.onend = () => { if (isHandsFree && !isPlaying && !isLoading) { try { recognitionRef.current.start(); setStatusMsg('Escuchando...'); } catch (e) {} } }; // Limpiar al desmontar return () => { if (recognitionRef.current) recognitionRef.current.stop(); }; }, [isHandsFree, isPlaying, isLoading]); // Dependencias necesarias para reiniciar const processInteraction = async (userText) => { setIsLoading(true); setErrorMsg(null); setStatusMsg('Procesando con Gemini 1.5 Flash...'); const key = apiKey.trim(); try { // 1. Llamada a Gemini con timeout const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 5000); const payloadText = { contents: [{ parts: [{ text: userText }] }], systemInstruction: { parts: [{ text: `Eres Kore, 28 años, Huesca. Labia, directa, sensual, disruptiva. Responde en español, máx 20 palabras. REGLA INNEGOCIABLE: CERO PREGUNTAS.` }] } }; const resText = await fetch( `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${key}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payloadText), signal: controller.signal } ); clearTimeout(timeoutId); if (!resText.ok) throw new Error(`Gemini error: ${resText.status}`); const dataText = await resText.json(); const aiText = dataText.candidates?.[0]?.content?.parts?.[0]?.text || "Mmm... vale."; setText(`Kore: ${aiText}`); // 2. Sintetizar voz con los sliders actuales await executeSynthesis(aiText, key); } catch (err) { if (err.name === 'AbortError') { setErrorMsg('Gemini timeout (5s)'); } else { setErrorMsg(err.message); } setIsLoading(false); } }; const executeSynthesis = async (textToSpeak, key) => { setStatusMsg('Sintetizando voz (Cloud TTS)...'); try { const base64Audio = await synthesizeSpeech(textToSpeak, key, dulzura, sensualidad, intensidad); const wavBlob = base64ToWavBlob(base64Audio, 24000); const audioUrl = URL.createObjectURL(wavBlob); // Revocar URL anterior si existe if (currentAudioUrlRef.current) { URL.revokeObjectURL(currentAudioUrlRef.current); } currentAudioUrlRef.current = audioUrl; activeAudioRef.current.src = audioUrl; activeAudioRef.current.onended = () => { setIsPlaying(false); setStatusMsg('Transmisión completada.'); if (isHandsFree) { try { recognitionRef.current.start(); setStatusMsg('Escuchando...'); } catch (e) {} } }; setStatusMsg('Transmitiendo...'); setIsPlaying(true); setIsLoading(false); await activeAudioRef.current.play().catch(err => { throw new Error(`Autoplay bloqueado: ${err.message}`); }); } catch (error) { throw new Error(`Fallo TTS: ${error.message}`); } }; const handleManualPlay = async () => { if (!text.trim()) return setErrorMsg('Escribe algo primero.'); // Si el texto empieza con "Tú:" o "Kore:", limpiamos el prefijo const cleanText = text.replace(/^(Tú:|Kore:)\s*/, ''); if (!cleanText.trim()) return setErrorMsg('Texto vacío después de limpiar.'); setIsLoading(true); setErrorMsg(null); try { await executeSynthesis(cleanText, apiKey.trim()); } catch (err) { setErrorMsg(err.message); setIsLoading(false); } }; const toggleHandsFree = () => { if (!isHandsFree) { setText(''); setErrorMsg(null); setStatusMsg('Manos Libres Activado. Habla...'); // Desbloquear audio en algunos navegadores if (activeAudioRef.current) { activeAudioRef.current.src = SILENT_WAV; activeAudioRef.current.play().catch(() => {}); } try { recognitionRef.current.start(); } catch (e) {} } else { if (activeAudioRef.current) { activeAudioRef.current.pause(); activeAudioRef.current.currentTime = 0; } setIsPlaying(false); setStatusMsg('Sistemas en pausa.'); if (recognitionRef.current) recognitionRef.current.stop(); } setIsHandsFree(!isHandsFree); }; const stopAudio = () => { if (activeAudioRef.current) { activeAudioRef.current.pause(); activeAudioRef.current.currentTime = 0; } setIsPlaying(false); setStatusMsg('Señal interrumpida.'); }; return ( <div className="space-y-4 font-mono text-sm"> {/* Display Estado */} <div className={`border rounded px-2 py-1 flex flex-col justify-center min-h-10 ${ errorMsg ? 'bg-red-950/50 border-red-900' : isHandsFree ? 'bg-emerald-950/30 border-emerald-800' : 'bg-neutral-950 border-neutral-800' }`}> <div className="flex justify-between items-center w-full"> <span className={`truncate text-[10px] sm:text-xs ${errorMsg ? 'text-red-500' : 'text-emerald-500'}`}> > {errorMsg || statusMsg} </span> {isPlaying && !errorMsg && <Activity size={14} className="text-emerald-500 animate-pulse ml-2 flex-shrink-0" />} {isLoading && !errorMsg && <Zap size={14} className="text-amber-500 animate-pulse ml-2 flex-shrink-0" />} {isHandsFree && !isPlaying && !isLoading && !errorMsg && <Mic size={14} className="text-red-500 animate-pulse ml-2 flex-shrink-0" />} </div> </div> {/* Input Texto / Log */} <textarea value={text} onChange={(e) => setText(e.target.value)} className="w-full bg-neutral-950/50 border border-neutral-700 rounded p-2 text-xs text-neutral-300 focus:outline-none focus:border-emerald-500 resize-none h-20" placeholder={isHandsFree ? "Escuchando transcripción en tiempo real..." : "Escribe texto directo o activa Manos Libres..."} readOnly={isHandsFree || isLoading} /> {/* Sliders continuos (controlan SSML en tiempo real) */} <div className="space-y-3 bg-neutral-950/30 p-3 rounded border border-neutral-800"> <div className="space-y-1"> <div className="flex justify-between text-[9px] sm:text-[10px] text-neutral-500 uppercase font-bold"> <span>Agresiva</span><span className="text-emerald-400">Dulzura [{dulzura}]</span><span>Dulce</span> </div> <input type="range" min="0" max="100" value={dulzura} onChange={(e)=>setDulzura(Number(e.target.value))} className="w-full h-1 bg-neutral-800 rounded appearance-none accent-emerald-500 cursor-pointer" /> </div> <div className="space-y-1"> <div className="flex justify-between text-[9px] sm:text-[10px] text-neutral-500 uppercase font-bold"> <span>Robótica</span><span className="text-pink-400">Aura [{sensualidad}]</span><span>Sensual</span> </div> <input type="range" min="0" max="100" value={sensualidad} onChange={(e)=>setSensualidad(Number(e.target.value))} className="w-full h-1 bg-neutral-800 rounded appearance-none accent-pink-500 cursor-pointer" /> </div> <div className="space-y-1"> <div className="flex justify-between text-[9px] sm:text-[10px] text-neutral-500 uppercase font-bold"> <span>Atenuada</span><span className="text-amber-400">Intensidad [{intensidad}]</span><span>Fuerte</span> </div> <input type="range" min="0" max="100" value={intensidad} onChange={(e)=>setIntensidad(Number(e.target.value))} className="w-full h-1 bg-neutral-800 rounded appearance-none accent-amber-500 cursor-pointer" /> </div> </div> {/* Botones de Control */} <div className="flex flex-col sm:flex-row gap-2"> <button onClick={toggleHandsFree} disabled={isLoading} className={`flex-1 py-2 rounded text-xs font-bold flex items-center justify-center gap-2 transition-colors border ${ isHandsFree ? 'bg-red-900/20 text-red-400 border-red-900/50 hover:bg-red-900/40 shadow-[0_0_10px_rgba(239,68,68,0.2)]' : 'bg-indigo-900/20 text-indigo-400 border-indigo-900/50 hover:bg-indigo-900/40' }`} > {isHandsFree ? <MicOff size={14} /> : <Mic size={14} />} {isHandsFree ? 'Detener Escucha' : 'Manos Libres'} </button> <div className="flex gap-2 flex-1"> <button onClick={handleManualPlay} disabled={isLoading || isPlaying || isHandsFree} className="flex-1 bg-emerald-600/20 hover:bg-emerald-600/40 text-emerald-400 border border-emerald-600/50 disabled:opacity-30 py-2 rounded text-xs font-bold flex items-center justify-center gap-1 transition-colors" > {isLoading ? <Loader2 size={14} className="animate-spin" /> : <Play size={14} />} Sintetizar </button> <button onClick={stopAudio} disabled={!isPlaying && !isHandsFree} className="px-4 bg-neutral-800 hover:bg-neutral-700 text-neutral-400 border border-neutral-700 disabled:opacity-30 py-2 rounded text-xs font-bold flex items-center justify-center transition-colors" > <Square size={14} /> </button> </div> </div> {/* Botón para limpiar caché (opcional) */} <div className="text-right"> <button onClick={() => audioCache.clear()} className="text-[8px] text-neutral-600 hover:text-neutral-400 underline" > limpiar caché de audio </button> </div> </div> ); }; // --- ENTORNO ESCRITORIO (sin cambios) --- export default function App() { const [widgets, setWidgets] = useState({ voice: { isOpen: true, pos: { x: window.innerWidth > 768 ? window.innerWidth / 2 - 170 : 20, y: 40 } } }); const toggleWidget = (id) => { setWidgets(prev => ({ ...prev, [id]: { ...prev[id], isOpen: !prev[id].isOpen } })); }; return ( <div className="w-full h-screen bg-neutral-950 bg-[radial-gradient(ellipse_80%_80%_at_50%_-20%,rgba(16,185,129,0.1),rgba(0,0,0,1))] overflow-hidden relative font-sans text-neutral-200"> <div className="absolute inset-0 flex items-center justify-center opacity-[0.02] pointer-events-none"><Settings2 size={500} /></div> {widgets.voice.isOpen && ( <DraggableWidget title="MODULADOR VOCAL KORE" icon={Zap} initialPos={widgets.voice.pos} onClose={() => toggleWidget('voice')}> <VoiceModulatorWidget /> </DraggableWidget> )} <div className="absolute bottom-6 left-1/2 transform -translate-x-1/2 bg-neutral-900/80 backdrop-blur-md border border-neutral-700/50 p-2 rounded-2xl shadow-2xl flex gap-2 z-[100]"> <div className="px-3 flex items-center border-r border-neutral-700/50 text-neutral-500"><LayoutGrid size={20} /></div> <button onClick={() => toggleWidget('voice')} className={`px-4 py-2 rounded-xl flex items-center gap-2 text-sm font-medium transition-all ${
Ultra-photorealistic cinematic film still of Alicia Vikander reimagined as the Grim Reaper, blending her natural beauty with an aura of haunting power and timeless authority. Her face is ethereal and striking, her sharp cheekbones and soft jawline accentuated by the cold, spectral glow of the surrounding light. Her skin is an ashen, porcelain hue, smooth yet unnervingly flawless, with faint, silvery veins visible just beneath the surface. Her deep brown eyes glow faintly with an otherworldly silver light, exuding both wisdom and an unrelenting inevitability. Her lips, slightly parted, are a dark, muted plum, adding to her haunting yet elegant appearance. Her hair is raven black, cascading in long, flowing waves that shimmer faintly as if touched by a supernatural breeze. Wisps of silvery mist weave through the strands, catching the dim light and creating an almost halo-like effect. Around her head hovers a faint, incorporeal crown of glowing runes, shifting and flickering like dying embers, signifying her dominion over life and death. She is dressed in an avant-garde interpretation of the Reaper’s cloak: a sleek, black, floor-length robe with intricate textures resembling flowing smoke and shadows. The fabric seems almost alive, shifting subtly as though it’s a part of the darkness itself. The edges of the cloak are frayed and dissolve into ethereal mist, giving her an otherworldly, intangible quality. Beneath the robe, glimpses of silvery armor etched with ancient, cryptic symbols are visible, hinting at her role as a celestial enforcer. Her hood is drawn back, revealing her face, but the shadows of the hood frame her features in a dramatic, gothic contrast. In her right hand, she wields a scythe unlike any other: its massive blade is forged from a gleaming black metal that reflects faint, ghostly images of souls. The staff is carved from a dark, polished wood entwined with glowing silver runes that pulse faintly, as though alive. Her left hand hovers slightly, trailing a faint mist of spectral energy that curls and dissipates into the surrounding air. The background is a surreal, otherworldly landscape: a vast, barren expanse shrouded in mist, with jagged, obsidian-like rock formations rising into the sky. The horizon glows faintly with an eerie, greenish-blue light, as if it’s the border between the world of the living and the dead. Shadowy silhouettes of wandering souls drift aimlessly in the distance, their faint whispers almost audible in the stillness. Above, the sky is a chaotic swirl of dark clouds, pierced by occasional streaks of ethereal light that illuminate the scene in fleeting bursts. The lighting is dramatic, with cold, pale blue and green tones dominating the scene, casting Alicia’s figure in sharp relief. The glowing runes on her armor and scythe cast subtle, shifting light patterns on her robes and the ground. Her face is illuminated by a soft, ghostly glow, emphasizing her beauty while adding an unnerving edge to her expression. Shadows play dynamically across her figure, enhancing the ethereal, otherworldly atmosphere. Her expression is calm and resolute, with a faint, enigmatic smile that suggests she understands the inevitability of her role. Her eyes convey both compassion and an uncompromising sense of duty, embodying the dual nature of the Grim Reaper as both a harbinger of death and a guide for lost souls. There’s a sense of timeless authority in her posture, as though she has walked the boundary between life and death for eternity. This ultra-photorealistic image is indistinguishable from a professional cinematic film still, with every detail—from the textures of her cloak and scythe to the eerie, atmospheric backdrop—rendered in breathtaking precision. The mood is chilling, majestic, and steeped in gothic gravitas.
a photorealistic limited edition sci-fi futuristic hovering jet powered shoe, NIKE, sparks, hovering, sci-fi, futuristic, jet powered, photograph, highly detailed, intricate, hyper realistic, ornate, luxury, cinematic, cgsociety, 8k Ultra Quality, sharp focus
a photorealistic limited edition sci-fi futuristic hovering jet powered shoe, NIKE, sparks, hovering, sci-fi, futuristic, jet powered, photograph, highly detailed, intricate, hyper realistic, ornate, luxury, cinematic, cgsociety, 8k Ultra Quality, sharp focus
An abstract, surreal painting of a hovering druid in an ancient forest clearing, surrounded by a glowing, atomic halo of vibrant orange light that forms swirling, concentric patterns around him. The druid’s white, flowing robes seem to blend into the air, merging with streaks of glowing energy and organic shapes, giving his form a soft, ethereal quality. His arms are extended outward, and instead of a defined face, his features are softened, almost dreamlike, as if part of the forest itself. The clearing is encircled by towering, ancient stones with glowing, fragmented runes etched into them. The runes emit a pulsing light that melts into warm, luminous trails that float upward, diffusing like ink in water. Around him, ethereal spirits appear as loose, wispy forms, their shapes abstracted and blending seamlessly with the flowing fog and mist. Their spectral trails leave fiery orange and red streaks that illuminate the air, creating a rhythm of light and color that guides the viewer’s eye through the piece. The forest background is painted in soft, blurred strokes, with deep shades of green, teal, and hints of shadowy blue. Light particles and embers float throughout the scene, giving it a textured, atmospheric quality. Godrays of diffused, golden light filter through the treetops, fragmenting as they pass through the mist and mingling with the energy surrounding the druid. The composition emphasizes motion and energy, with swirling patterns and soft lines blending into each other, capturing a sense of mystery and ancient magic in a surreal and dreamlike manner." Key Elements with Weights (Abstract, surreal painting style:1.6) (Druid in white robes, hovering, ethereal form:1.5) (Atomic halo of vibrant orange light:1.7) (Glowing, fragmented runes on ancient stones:1.4) (Spectral spirits with abstract, fiery orange trails:1.5) (Soft, blurred forest with deep green and blue hues:1.3) (Volumetric godrays fragmented by the mist:1.4) (Flowing embers and light particles for texture:1.3)
In a desolate, rocky volcanic landscape filled with jagged, black volcanic rocks and deep craters scattered across the ground, a lone man stands with his arms outstretched to his sides, gazing directly into the camera with an intense expression. The ground around him is covered by a thin layer of smoke or mist that hovers about 15 cm above the surface, partially obscuring the rugged terrain. Subtle veins of glowing lava pulse beneath the smoke, casting a faint, eerie orange-red light that makes the mist appear to glow and ripple softly across the rocks. Above his head, a floating atomic symbol—exactly like the classic atomic design with three orbits circling a central nucleus—glows in a vibrant atomic green, emanating a powerful, supernatural light. This emblem seems to represent an almost mystical source of energy, hovering just above him and illuminating his face with a greenish hue. From his eyes and mouth, a slow, controlled stream of molten lava flows, pouring out like fiery tears and a searing, molten torrent. The lava glows in a deep, vivid orange and flows down in thick rivulets, casting flickering shadows and illuminating the contours of his face. The intense heat radiates from his body, causing wisps of smoke and heat waves to rise from his skin, blending into the smoky ground fog. Background and Surroundings: The volcanic landscape stretches endlessly, with dark, sharp rocks and lava veins glowing subtly through the mist, adding a mysterious depth to the scene. The subtle interplay of the green atomic glow above him and the warm lava light below creates a haunting, otherworldly atmosphere. Embers and faint particles of ash drift lazily in the air, catching the light from the lava and adding an ethereal quality. Rendering Style: Hyperrealistic with intricate, volumetric lighting, emphasizing the textures of the rocks, mist, and the glowing atomic symbol. Every detail, from the flowing lava and subtle ground lighting to the distinct atomic symbol glowing in green above his head, is meticulously rendered, creating a powerful and surreal visual. Ideal for ArtStation, this masterpiece balances the mysticism of the atomic symbol with the raw power of volcanic energy, captured in an epic cinematic composition.
A hyper-realistic, cinematic image of a lone druid standing atop a rugged mountain peak, dressed in ancient, flowing robes with intricate patterns. The druid is surrounded by swirling mist and fog, illuminated by an eerie, glowing (orange orb hovering above his head:1.7) that casts warm, ethereal light over his face and attire. His outstretched arms are raised as he conjures spirits from the ground below, their ghostly forms emerging from the rock as if rising from the depths of the earth. The spirits are ethereal and translucent, their forms stretching and swirling as they ascend around the druid, leaving (trails of glowing orange light:1.6) in the mist. These spectral figures have soft, flowing shapes that merge with the fog, creating a hauntingly beautiful display of light and shadow. The trails of orange glow fade into the mist, adding an otherworldly effect that enhances the supernatural energy of the scene. The mountain peak is rugged, with jagged rocks and a vast landscape stretching out into the distance. Dark clouds loom overhead, partially obscuring the sky, while volumetric godrays break through in scattered beams, adding dramatic lighting that illuminates the scene and emphasizes the swirling fog. The air is thick with floating particles and a soft, chilling mist that clings to the ground, giving the scene an ancient, mystical feel. The entire image captures the power and mystery of the druid’s ritual, with the vivid contrast of orange light against the dark, foggy landscape creating a cinematic and awe-inspiring atmosphere." Key Elements with Weights: (Hyper-realistic, cinematic atmosphere:1.5) (Ancient druidic clothing, intricate robes and symbols:1.5) (Glowing orange orb hovering above his head:1.7) (Ethereal spirits rising from the ground, translucent forms:1.6) (Orange trails left by the spirits in the mist and fog:1.6) (Volumetric godrays, dynamic lighting through fog:1.4) (Dark, rugged mountain peak with jagged rocks:1.3) (Chilling, supernatural atmosphere:1.5) Abstract Painting Prompt Prompt: "An abstract, mystical painting of a druid standing on top of a jagged mountain, conjuring ethereal spirits from the ground below. The druid is adorned in ancient robes with intricate designs, his figure partially obscured by swirling mist and fog. Above his head floats a (glowing orange orb:1.7), casting an otherworldly light over his face and clothing, and adding a surreal, magical quality to the scene. The spirits he summons rise from the rocky ground, their forms ghostly and translucent, flowing upwards in long, graceful trails that (leave vibrant orange marks in the mist:1.6). The spirits blend into the fog with a dreamlike, almost liquid motion, their shapes abstracted and fluid. The orange trails contrast with the dark, moody fog, adding a mystical energy to the painting. The mountain peak is depicted with dark, textured brushstrokes, blending into a foggy expanse that stretches into the distance. Soft, diffused godrays pierce through the mist, casting light across the druid and the spirits, creating depth and a sense of motion in the swirling fog. The overall composition is both haunting and enchanting, capturing a moment of ancient magic with surreal colors and abstract forms that emphasize the beauty and power of the druid’s ritual."
A high-angle, full-body anime illustration of a beautiful sorceress hovering in mid-air. The perspective is looking down at her from above. She has long black hair, purple eyes, and a silver cross earring. She is wearing a high-cut black bodysuit with a sheer fishnet halter-neck and ornate silver metallic armor plating on the bust. Her skin is rendered with a hyper-realistic wet, glossy texture with strong highlights. She poses dynamically with knees bent and arms outstretched downwards, fingers splayed. Behind her, large four-pointed silver metallic shuriken blades float in a radial pattern. The background is a dark starry void with grey rocks visible far below. Masterpiece, 8k resolution, dynamic composition, shiny metal textures. High-angle shot, bird's-eye view, hovering pose, wet skin texture, glossy latex, floating weapons, shuriken, starry night, anime semi-realism, cinematic lighting.
Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3 Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3
Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3 Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3
He optimizado tu código para lograr una modulación vocal continua y fluida basada en los sliders, con caché de audio, timeouts y mejor manejo del estado. Ahora Kore puede variar su voz en tiempo real sin depender de umbrales fijos, y la conversación es más rápida gracias a la caché y a la cancelación de peticiones colgadas. ```javascript import React, { useState, useRef, useEffect, useCallback } from 'react'; import { Play, Square, Mic, MicOff, Settings2, Activity, Loader2, X, GripHorizontal, LayoutGrid, Zap, AlertCircle } from 'lucide-react'; // --- CONSTANTES --- const SILENT_WAV = "data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA"; const TTS_TIMEOUT = 5000; // 5 segundos máximo para la síntesis const DEFAULT_API_KEY = 'AIzaSyBlkvy_Op-XlzSMSDDl9ip42dMFZX28MAA'; // ⚠️ Cámbiala por tu propia clave // --- UTILIDADES --- const base64ToWavBlob = (base64Data, sampleRate = 24000) => { const binaryString = window.atob(base64Data); const pcmData = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) pcmData[i] = binaryString.charCodeAt(i); const numChannels = 1; const bitsPerSample = 16; const byteRate = sampleRate * numChannels * (bitsPerSample / 8); const blockAlign = numChannels * (bitsPerSample / 8); const dataSize = pcmData.length; const buffer = new ArrayBuffer(44 + dataSize); const view = new DataView(buffer); const writeString = (view, offset, string) => { for (let i = 0; i < string.length; i++) view.setUint8(offset + i, string.charCodeAt(i)); }; writeString(view, 0, 'RIFF'); view.setUint32(4, 36 + dataSize, true); writeString(view, 8, 'WAVE'); writeString(view, 12, 'fmt '); view.setUint32(16, 16, true); view.setUint16(20, 1, true); view.setUint16(22, numChannels, true); view.setUint32(24, sampleRate, true); view.setUint32(28, byteRate, true); view.setUint16(32, blockAlign, true); view.setUint16(34, bitsPerSample, true); writeString(view, 36, 'data'); view.setUint32(40, dataSize, true); for (let i = 0; i < dataSize; i++) view.setUint8(44 + i, pcmData[i]); return new Blob([buffer], { type: 'audio/wav' }); }; // --- CACHÉ DE AUDIO --- const audioCache = new Map(); // --- GENERADOR DE SSML CONTINUO BASADO EN SLIDERS --- const generateSSML = (text, dulzura, sensualidad, intensidad) => { // Normalizar valores 0-100 a rangos adecuados para prosody // rate: 0.5 a 2.0 (1.0 es normal) const rate = 0.8 + (intensidad / 100) * 1.2; // 0.8 (lento) a 2.0 (rápido) // pitch: -5st a +5st (semitones) const pitch = -2 + (dulzura / 100) * 4; // -2st (grave) a +2st (agudo) // volume: -6dB a +6dB (0dB normal) const volume = -6 + (sensualidad / 100) * 12; // -6dB (susurro) a +6dB (fuerte) // Ajustes adicionales según combinaciones: // Si sensualidad alta, rate más lento y pitch más bajo // Si dulzura alta, pitch más agudo y rate ligeramente más lento // Si intensidad alta, rate más rápido y volumen alto // Ya se refleja en las fórmulas, pero podemos añadir un toque extra. const ssml = `<speak> <prosody rate="${rate.toFixed(2)}" pitch="${pitch.toFixed(0)}st" volume="${volume.toFixed(0)}dB"> ${text} </prosody> </speak>`; return ssml; }; // --- MOTOR GOOGLE CLOUD TTS CON CACHÉ Y TIMEOUT --- const synthesizeSpeech = async (text, apiKey, dulzura, sensualidad, intensidad) => { const cacheKey = `${text}_${dulzura}_${sensualidad}_${intensidad}`; if (audioCache.has(cacheKey)) { console.log('🎯 Usando audio cacheado'); return audioCache.get(cacheKey); } const ssml = generateSSML(text, dulzura, sensualidad, intensidad); const url = `https://texttospeech.googleapis.com/v1/text:synthesize?key=${apiKey}`; const body = { input: { ssml }, voice: { languageCode: 'es-ES', name: 'es-ES-Neural2-F', ssmlGender: 'FEMALE' }, audioConfig: { audioEncoding: 'LINEAR16', sampleRateHertz: 24000 } }; const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), TTS_TIMEOUT); try { const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), signal: controller.signal }); clearTimeout(timeoutId); if (!res.ok) throw new Error(`TTS error: ${res.status}`); const data = await res.json(); audioCache.set(cacheKey, data.audioContent); return data.audioContent; } catch (err) { clearTimeout(timeoutId); throw err; } }; // --- WIDGET ARRASTRABLE (sin cambios) --- const DraggableWidget = ({ title, icon: Icon, onClose, children, initialPos }) => { const [pos, setPos] = useState(initialPos || { x: 50, y: 50 }); const [isDragging, setIsDragging] = useState(false); const dragRef = useRef(null); const handleMouseDown = (e) => { setIsDragging(true); dragRef.current = { startX: e.clientX, startY: e.clientY, initialX: pos.x, initialY: pos.y }; }; const handleMouseMove = (e) => { if (!isDragging) return; setPos({ x: Math.max(0, dragRef.current.initialX + (e.clientX - dragRef.current.startX)), y: Math.max(0, dragRef.current.initialY + (e.clientY - dragRef.current.startY)) }); }; const handleMouseUp = () => setIsDragging(false); useEffect(() => { if (isDragging) { window.addEventListener('mousemove', handleMouseMove); window.addEventListener('mouseup', handleMouseUp); } return () => { window.removeEventListener('mousemove', handleMouseMove); window.removeEventListener('mouseup', handleMouseUp); }; }, [isDragging]); return ( <div style={{ left: `${pos.x}px`, top: `${pos.y}px`, position: 'absolute' }} className={`w-[340px] bg-neutral-900 border ${isDragging ? 'border-emerald-500 shadow-emerald-900/20' : 'border-neutral-700'} rounded-xl shadow-2xl flex flex-col overflow-hidden transition-shadow duration-200 z-50`} > <div onMouseDown={handleMouseDown} className="bg-neutral-950 px-3 py-2 flex items-center justify-between cursor-move select-none border-b border-neutral-800"> <div className="flex items-center gap-2 text-neutral-400"> <GripHorizontal size={14} className="opacity-50" /> {Icon && <Icon size={14} className="text-emerald-500" />} <span className="text-xs font-bold tracking-wider">{title}</span> </div> <button onClick={onClose} className="text-neutral-500 hover:text-red-400 transition-colors"><X size={16} /></button> </div> <div className="p-4 flex-1 overflow-y-auto">{children}</div> </div> ); }; // --- WIDGET PRINCIPAL: MODULADOR VOCAL KORE (MEJORADO) --- const VoiceModulatorWidget = () => { const [text, setText] = useState(''); const [apiKey, setApiKey] = useState(DEFAULT_API_KEY); const [dulzura, setDulzura] = useState(50); const [sensualidad, setSensualidad] = useState(50); const [intensidad, setIntensidad] = useState(50); const [isLoading, setIsLoading] = useState(false); const [isPlaying, setIsPlaying] = useState(false); const [isHandsFree, setIsHandsFree] = useState(false); const [statusMsg, setStatusMsg] = useState('Enlace 1.5 Flash + GCP TTS Establecido.'); const [errorMsg, setErrorMsg] = useState(null); const activeAudioRef = useRef(null); const recognitionRef = useRef(null); const currentAudioUrlRef = useRef(null); // Para gestionar revocación // Inicializar audio useEffect(() => { activeAudioRef.current = new Audio(); activeAudioRef.current.preload = "auto"; return () => { if (activeAudioRef.current) { activeAudioRef.current.pause(); if (currentAudioUrlRef.current) { URL.revokeObjectURL(currentAudioUrlRef.current); } } if (recognitionRef.current) recognitionRef.current.stop(); }; }, []); // Configurar reconocimiento de voz useEffect(() => { if (!('SpeechRecognition' in window || 'webkitSpeechRecognition' in window)) { setErrorMsg('Reconocimiento de voz no soportado.'); return; } const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; recognitionRef.current = new SpeechRecognition(); recognitionRef.current.lang = 'es-ES'; recognitionRef.current.interimResults = false; recognitionRef.current.continuous = false; recognitionRef.current.onresult = async (event) => { const transcript = event.results[0][0].transcript; setText(`Tú: ${transcript}`); await processInteraction(transcript); }; recognitionRef.current.onerror = (event) => { if (event.error !== 'no-speech') { setErrorMsg(`Micrófono: ${event.error}`); setIsHandsFree(false); } }; recognitionRef.current.onend = () => { if (isHandsFree && !isPlaying && !isLoading) { try { recognitionRef.current.start(); setStatusMsg('Escuchando...'); } catch (e) {} } }; // Limpiar al desmontar return () => { if (recognitionRef.current) recognitionRef.current.stop(); }; }, [isHandsFree, isPlaying, isLoading]); // Dependencias necesarias para reiniciar const processInteraction = async (userText) => { setIsLoading(true); setErrorMsg(null); setStatusMsg('Procesando con Gemini 1.5 Flash...'); const key = apiKey.trim(); try { // 1. Llamada a Gemini con timeout const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 5000); const payloadText = { contents: [{ parts: [{ text: userText }] }], systemInstruction: { parts: [{ text: `Eres Kore, 28 años, Huesca. Labia, directa, sensual, disruptiva. Responde en español, máx 20 palabras. REGLA INNEGOCIABLE: CERO PREGUNTAS.` }] } }; const resText = await fetch( `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${key}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payloadText), signal: controller.signal } ); clearTimeout(timeoutId); if (!resText.ok) throw new Error(`Gemini error: ${resText.status}`); const dataText = await resText.json(); const aiText = dataText.candidates?.[0]?.content?.parts?.[0]?.text || "Mmm... vale."; setText(`Kore: ${aiText}`); // 2. Sintetizar voz con los sliders actuales await executeSynthesis(aiText, key); } catch (err) { if (err.name === 'AbortError') { setErrorMsg('Gemini timeout (5s)'); } else { setErrorMsg(err.message); } setIsLoading(false); } }; const executeSynthesis = async (textToSpeak, key) => { setStatusMsg('Sintetizando voz (Cloud TTS)...'); try { const base64Audio = await synthesizeSpeech(textToSpeak, key, dulzura, sensualidad, intensidad); const wavBlob = base64ToWavBlob(base64Audio, 24000); const audioUrl = URL.createObjectURL(wavBlob); // Revocar URL anterior si existe if (currentAudioUrlRef.current) { URL.revokeObjectURL(currentAudioUrlRef.current); } currentAudioUrlRef.current = audioUrl; activeAudioRef.current.src = audioUrl; activeAudioRef.current.onended = () => { setIsPlaying(false); setStatusMsg('Transmisión completada.'); if (isHandsFree) { try { recognitionRef.current.start(); setStatusMsg('Escuchando...'); } catch (e) {} } }; setStatusMsg('Transmitiendo...'); setIsPlaying(true); setIsLoading(false); await activeAudioRef.current.play().catch(err => { throw new Error(`Autoplay bloqueado: ${err.message}`); }); } catch (error) { throw new Error(`Fallo TTS: ${error.message}`); } }; const handleManualPlay = async () => { if (!text.trim()) return setErrorMsg('Escribe algo primero.'); // Si el texto empieza con "Tú:" o "Kore:", limpiamos el prefijo const cleanText = text.replace(/^(Tú:|Kore:)\s*/, ''); if (!cleanText.trim()) return setErrorMsg('Texto vacío después de limpiar.'); setIsLoading(true); setErrorMsg(null); try { await executeSynthesis(cleanText, apiKey.trim()); } catch (err) { setErrorMsg(err.message); setIsLoading(false); } }; const toggleHandsFree = () => { if (!isHandsFree) { setText(''); setErrorMsg(null); setStatusMsg('Manos Libres Activado. Habla...'); // Desbloquear audio en algunos navegadores if (activeAudioRef.current) { activeAudioRef.current.src = SILENT_WAV; activeAudioRef.current.play().catch(() => {}); } try { recognitionRef.current.start(); } catch (e) {} } else { if (activeAudioRef.current) { activeAudioRef.current.pause(); activeAudioRef.current.currentTime = 0; } setIsPlaying(false); setStatusMsg('Sistemas en pausa.'); if (recognitionRef.current) recognitionRef.current.stop(); } setIsHandsFree(!isHandsFree); }; const stopAudio = () => { if (activeAudioRef.current) { activeAudioRef.current.pause(); activeAudioRef.current.currentTime = 0; } setIsPlaying(false); setStatusMsg('Señal interrumpida.'); }; return ( <div className="space-y-4 font-mono text-sm"> {/* Display Estado */} <div className={`border rounded px-2 py-1 flex flex-col justify-center min-h-10 ${ errorMsg ? 'bg-red-950/50 border-red-900' : isHandsFree ? 'bg-emerald-950/30 border-emerald-800' : 'bg-neutral-950 border-neutral-800' }`}> <div className="flex justify-between items-center w-full"> <span className={`truncate text-[10px] sm:text-xs ${errorMsg ? 'text-red-500' : 'text-emerald-500'}`}> > {errorMsg || statusMsg} </span> {isPlaying && !errorMsg && <Activity size={14} className="text-emerald-500 animate-pulse ml-2 flex-shrink-0" />} {isLoading && !errorMsg && <Zap size={14} className="text-amber-500 animate-pulse ml-2 flex-shrink-0" />} {isHandsFree && !isPlaying && !isLoading && !errorMsg && <Mic size={14} className="text-red-500 animate-pulse ml-2 flex-shrink-0" />} </div> </div> {/* Input Texto / Log */} <textarea value={text} onChange={(e) => setText(e.target.value)} className="w-full bg-neutral-950/50 border border-neutral-700 rounded p-2 text-xs text-neutral-300 focus:outline-none focus:border-emerald-500 resize-none h-20" placeholder={isHandsFree ? "Escuchando transcripción en tiempo real..." : "Escribe texto directo o activa Manos Libres..."} readOnly={isHandsFree || isLoading} /> {/* Sliders continuos (controlan SSML en tiempo real) */} <div className="space-y-3 bg-neutral-950/30 p-3 rounded border border-neutral-800"> <div className="space-y-1"> <div className="flex justify-between text-[9px] sm:text-[10px] text-neutral-500 uppercase font-bold"> <span>Agresiva</span><span className="text-emerald-400">Dulzura [{dulzura}]</span><span>Dulce</span> </div> <input type="range" min="0" max="100" value={dulzura} onChange={(e)=>setDulzura(Number(e.target.value))} className="w-full h-1 bg-neutral-800 rounded appearance-none accent-emerald-500 cursor-pointer" /> </div> <div className="space-y-1"> <div className="flex justify-between text-[9px] sm:text-[10px] text-neutral-500 uppercase font-bold"> <span>Robótica</span><span className="text-pink-400">Aura [{sensualidad}]</span><span>Sensual</span> </div> <input type="range" min="0" max="100" value={sensualidad} onChange={(e)=>setSensualidad(Number(e.target.value))} className="w-full h-1 bg-neutral-800 rounded appearance-none accent-pink-500 cursor-pointer" /> </div> <div className="space-y-1"> <div className="flex justify-between text-[9px] sm:text-[10px] text-neutral-500 uppercase font-bold"> <span>Atenuada</span><span className="text-amber-400">Intensidad [{intensidad}]</span><span>Fuerte</span> </div> <input type="range" min="0" max="100" value={intensidad} onChange={(e)=>setIntensidad(Number(e.target.value))} className="w-full h-1 bg-neutral-800 rounded appearance-none accent-amber-500 cursor-pointer" /> </div> </div> {/* Botones de Control */} <div className="flex flex-col sm:flex-row gap-2"> <button onClick={toggleHandsFree} disabled={isLoading} className={`flex-1 py-2 rounded text-xs font-bold flex items-center justify-center gap-2 transition-colors border ${ isHandsFree ? 'bg-red-900/20 text-red-400 border-red-900/50 hover:bg-red-900/40 shadow-[0_0_10px_rgba(239,68,68,0.2)]' : 'bg-indigo-900/20 text-indigo-400 border-indigo-900/50 hover:bg-indigo-900/40' }`} > {isHandsFree ? <MicOff size={14} /> : <Mic size={14} />} {isHandsFree ? 'Detener Escucha' : 'Manos Libres'} </button> <div className="flex gap-2 flex-1"> <button onClick={handleManualPlay} disabled={isLoading || isPlaying || isHandsFree} className="flex-1 bg-emerald-600/20 hover:bg-emerald-600/40 text-emerald-400 border border-emerald-600/50 disabled:opacity-30 py-2 rounded text-xs font-bold flex items-center justify-center gap-1 transition-colors" > {isLoading ? <Loader2 size={14} className="animate-spin" /> : <Play size={14} />} Sintetizar </button> <button onClick={stopAudio} disabled={!isPlaying && !isHandsFree} className="px-4 bg-neutral-800 hover:bg-neutral-700 text-neutral-400 border border-neutral-700 disabled:opacity-30 py-2 rounded text-xs font-bold flex items-center justify-center transition-colors" > <Square size={14} /> </button> </div> </div> {/* Botón para limpiar caché (opcional) */} <div className="text-right"> <button onClick={() => audioCache.clear()} className="text-[8px] text-neutral-600 hover:text-neutral-400 underline" > limpiar caché de audio </button> </div> </div> ); }; // --- ENTORNO ESCRITORIO (sin cambios) --- export default function App() { const [widgets, setWidgets] = useState({ voice: { isOpen: true, pos: { x: window.innerWidth > 768 ? window.innerWidth / 2 - 170 : 20, y: 40 } } }); const toggleWidget = (id) => { setWidgets(prev => ({ ...prev, [id]: { ...prev[id], isOpen: !prev[id].isOpen } })); }; return ( <div className="w-full h-screen bg-neutral-950 bg-[radial-gradient(ellipse_80%_80%_at_50%_-20%,rgba(16,185,129,0.1),rgba(0,0,0,1))] overflow-hidden relative font-sans text-neutral-200"> <div className="absolute inset-0 flex items-center justify-center opacity-[0.02] pointer-events-none"><Settings2 size={500} /></div> {widgets.voice.isOpen && ( <DraggableWidget title="MODULADOR VOCAL KORE" icon={Zap} initialPos={widgets.voice.pos} onClose={() => toggleWidget('voice')}> <VoiceModulatorWidget /> </DraggableWidget> )} <div className="absolute bottom-6 left-1/2 transform -translate-x-1/2 bg-neutral-900/80 backdrop-blur-md border border-neutral-700/50 p-2 rounded-2xl shadow-2xl flex gap-2 z-[100]"> <div className="px-3 flex items-center border-r border-neutral-700/50 text-neutral-500"><LayoutGrid size={20} /></div> <button onClick={() => toggleWidget('voice')} className={`px-4 py-2 rounded-xl flex items-center gap-2 text-sm font-medium transition-all ${
An abstract, surreal painting of a hovering druid in an ancient forest clearing, surrounded by a glowing, atomic halo of vibrant orange light that forms swirling, concentric patterns around him. The druid’s white, flowing robes seem to blend into the air, merging with streaks of glowing energy and organic shapes, giving his form a soft, ethereal quality. His arms are extended outward, and instead of a defined face, his features are softened, almost dreamlike, as if part of the forest itself. The clearing is encircled by towering, ancient stones with glowing, fragmented runes etched into them. The runes emit a pulsing light that melts into warm, luminous trails that float upward, diffusing like ink in water. Around him, ethereal spirits appear as loose, wispy forms, their shapes abstracted and blending seamlessly with the flowing fog and mist. Their spectral trails leave fiery orange and red streaks that illuminate the air, creating a rhythm of light and color that guides the viewer’s eye through the piece. The forest background is painted in soft, blurred strokes, with deep shades of green, teal, and hints of shadowy blue. Light particles and embers float throughout the scene, giving it a textured, atmospheric quality. Godrays of diffused, golden light filter through the treetops, fragmenting as they pass through the mist and mingling with the energy surrounding the druid. The composition emphasizes motion and energy, with swirling patterns and soft lines blending into each other, capturing a sense of mystery and ancient magic in a surreal and dreamlike manner." Key Elements with Weights (Abstract, surreal painting style:1.6) (Druid in white robes, hovering, ethereal form:1.5) (Atomic halo of vibrant orange light:1.7) (Glowing, fragmented runes on ancient stones:1.4) (Spectral spirits with abstract, fiery orange trails:1.5) (Soft, blurred forest with deep green and blue hues:1.3) (Volumetric godrays fragmented by the mist:1.4) (Flowing embers and light particles for texture:1.3)
Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3 Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3
In a desolate, rocky volcanic landscape filled with jagged, black volcanic rocks and deep craters scattered across the ground, a lone man stands with his arms outstretched to his sides, gazing directly into the camera with an intense expression. The ground around him is covered by a thin layer of smoke or mist that hovers about 15 cm above the surface, partially obscuring the rugged terrain. Subtle veins of glowing lava pulse beneath the smoke, casting a faint, eerie orange-red light that makes the mist appear to glow and ripple softly across the rocks. Above his head, a floating atomic symbol—exactly like the classic atomic design with three orbits circling a central nucleus—glows in a vibrant atomic green, emanating a powerful, supernatural light. This emblem seems to represent an almost mystical source of energy, hovering just above him and illuminating his face with a greenish hue. From his eyes and mouth, a slow, controlled stream of molten lava flows, pouring out like fiery tears and a searing, molten torrent. The lava glows in a deep, vivid orange and flows down in thick rivulets, casting flickering shadows and illuminating the contours of his face. The intense heat radiates from his body, causing wisps of smoke and heat waves to rise from his skin, blending into the smoky ground fog. Background and Surroundings: The volcanic landscape stretches endlessly, with dark, sharp rocks and lava veins glowing subtly through the mist, adding a mysterious depth to the scene. The subtle interplay of the green atomic glow above him and the warm lava light below creates a haunting, otherworldly atmosphere. Embers and faint particles of ash drift lazily in the air, catching the light from the lava and adding an ethereal quality. Rendering Style: Hyperrealistic with intricate, volumetric lighting, emphasizing the textures of the rocks, mist, and the glowing atomic symbol. Every detail, from the flowing lava and subtle ground lighting to the distinct atomic symbol glowing in green above his head, is meticulously rendered, creating a powerful and surreal visual. Ideal for ArtStation, this masterpiece balances the mysticism of the atomic symbol with the raw power of volcanic energy, captured in an epic cinematic composition.
A hyper-realistic, cinematic image of a lone druid standing atop a rugged mountain peak, dressed in ancient, flowing robes with intricate patterns. The druid is surrounded by swirling mist and fog, illuminated by an eerie, glowing (orange orb hovering above his head:1.7) that casts warm, ethereal light over his face and attire. His outstretched arms are raised as he conjures spirits from the ground below, their ghostly forms emerging from the rock as if rising from the depths of the earth. The spirits are ethereal and translucent, their forms stretching and swirling as they ascend around the druid, leaving (trails of glowing orange light:1.6) in the mist. These spectral figures have soft, flowing shapes that merge with the fog, creating a hauntingly beautiful display of light and shadow. The trails of orange glow fade into the mist, adding an otherworldly effect that enhances the supernatural energy of the scene. The mountain peak is rugged, with jagged rocks and a vast landscape stretching out into the distance. Dark clouds loom overhead, partially obscuring the sky, while volumetric godrays break through in scattered beams, adding dramatic lighting that illuminates the scene and emphasizes the swirling fog. The air is thick with floating particles and a soft, chilling mist that clings to the ground, giving the scene an ancient, mystical feel. The entire image captures the power and mystery of the druid’s ritual, with the vivid contrast of orange light against the dark, foggy landscape creating a cinematic and awe-inspiring atmosphere." Key Elements with Weights: (Hyper-realistic, cinematic atmosphere:1.5) (Ancient druidic clothing, intricate robes and symbols:1.5) (Glowing orange orb hovering above his head:1.7) (Ethereal spirits rising from the ground, translucent forms:1.6) (Orange trails left by the spirits in the mist and fog:1.6) (Volumetric godrays, dynamic lighting through fog:1.4) (Dark, rugged mountain peak with jagged rocks:1.3) (Chilling, supernatural atmosphere:1.5) Abstract Painting Prompt Prompt: "An abstract, mystical painting of a druid standing on top of a jagged mountain, conjuring ethereal spirits from the ground below. The druid is adorned in ancient robes with intricate designs, his figure partially obscured by swirling mist and fog. Above his head floats a (glowing orange orb:1.7), casting an otherworldly light over his face and clothing, and adding a surreal, magical quality to the scene. The spirits he summons rise from the rocky ground, their forms ghostly and translucent, flowing upwards in long, graceful trails that (leave vibrant orange marks in the mist:1.6). The spirits blend into the fog with a dreamlike, almost liquid motion, their shapes abstracted and fluid. The orange trails contrast with the dark, moody fog, adding a mystical energy to the painting. The mountain peak is depicted with dark, textured brushstrokes, blending into a foggy expanse that stretches into the distance. Soft, diffused godrays pierce through the mist, casting light across the druid and the spirits, creating depth and a sense of motion in the swirling fog. The overall composition is both haunting and enchanting, capturing a moment of ancient magic with surreal colors and abstract forms that emphasize the beauty and power of the druid’s ritual."
Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3 Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3
Ultra-photorealistic cinematic film still of Alicia Vikander reimagined as the Grim Reaper, blending her natural beauty with an aura of haunting power and timeless authority. Her face is ethereal and striking, her sharp cheekbones and soft jawline accentuated by the cold, spectral glow of the surrounding light. Her skin is an ashen, porcelain hue, smooth yet unnervingly flawless, with faint, silvery veins visible just beneath the surface. Her deep brown eyes glow faintly with an otherworldly silver light, exuding both wisdom and an unrelenting inevitability. Her lips, slightly parted, are a dark, muted plum, adding to her haunting yet elegant appearance. Her hair is raven black, cascading in long, flowing waves that shimmer faintly as if touched by a supernatural breeze. Wisps of silvery mist weave through the strands, catching the dim light and creating an almost halo-like effect. Around her head hovers a faint, incorporeal crown of glowing runes, shifting and flickering like dying embers, signifying her dominion over life and death. She is dressed in an avant-garde interpretation of the Reaper’s cloak: a sleek, black, floor-length robe with intricate textures resembling flowing smoke and shadows. The fabric seems almost alive, shifting subtly as though it’s a part of the darkness itself. The edges of the cloak are frayed and dissolve into ethereal mist, giving her an otherworldly, intangible quality. Beneath the robe, glimpses of silvery armor etched with ancient, cryptic symbols are visible, hinting at her role as a celestial enforcer. Her hood is drawn back, revealing her face, but the shadows of the hood frame her features in a dramatic, gothic contrast. In her right hand, she wields a scythe unlike any other: its massive blade is forged from a gleaming black metal that reflects faint, ghostly images of souls. The staff is carved from a dark, polished wood entwined with glowing silver runes that pulse faintly, as though alive. Her left hand hovers slightly, trailing a faint mist of spectral energy that curls and dissipates into the surrounding air. The background is a surreal, otherworldly landscape: a vast, barren expanse shrouded in mist, with jagged, obsidian-like rock formations rising into the sky. The horizon glows faintly with an eerie, greenish-blue light, as if it’s the border between the world of the living and the dead. Shadowy silhouettes of wandering souls drift aimlessly in the distance, their faint whispers almost audible in the stillness. Above, the sky is a chaotic swirl of dark clouds, pierced by occasional streaks of ethereal light that illuminate the scene in fleeting bursts. The lighting is dramatic, with cold, pale blue and green tones dominating the scene, casting Alicia’s figure in sharp relief. The glowing runes on her armor and scythe cast subtle, shifting light patterns on her robes and the ground. Her face is illuminated by a soft, ghostly glow, emphasizing her beauty while adding an unnerving edge to her expression. Shadows play dynamically across her figure, enhancing the ethereal, otherworldly atmosphere. Her expression is calm and resolute, with a faint, enigmatic smile that suggests she understands the inevitability of her role. Her eyes convey both compassion and an uncompromising sense of duty, embodying the dual nature of the Grim Reaper as both a harbinger of death and a guide for lost souls. There’s a sense of timeless authority in her posture, as though she has walked the boundary between life and death for eternity. This ultra-photorealistic image is indistinguishable from a professional cinematic film still, with every detail—from the textures of her cloak and scythe to the eerie, atmospheric backdrop—rendered in breathtaking precision. The mood is chilling, majestic, and steeped in gothic gravitas.
A high-angle, full-body anime illustration of a beautiful sorceress hovering in mid-air. The perspective is looking down at her from above. She has long black hair, purple eyes, and a silver cross earring. She is wearing a high-cut black bodysuit with a sheer fishnet halter-neck and ornate silver metallic armor plating on the bust. Her skin is rendered with a hyper-realistic wet, glossy texture with strong highlights. She poses dynamically with knees bent and arms outstretched downwards, fingers splayed. Behind her, large four-pointed silver metallic shuriken blades float in a radial pattern. The background is a dark starry void with grey rocks visible far below. Masterpiece, 8k resolution, dynamic composition, shiny metal textures. High-angle shot, bird's-eye view, hovering pose, wet skin texture, glossy latex, floating weapons, shuriken, starry night, anime semi-realism, cinematic lighting.
a photorealistic limited edition sci-fi futuristic hovering jet powered shoe, NIKE, sparks, hovering, sci-fi, futuristic, jet powered, photograph, highly detailed, intricate, hyper realistic, ornate, luxury, cinematic, cgsociety, 8k Ultra Quality, sharp focus
a photorealistic limited edition sci-fi futuristic hovering jet powered shoe, NIKE, sparks, hovering, sci-fi, futuristic, jet powered, photograph, highly detailed, intricate, hyper realistic, ornate, luxury, cinematic, cgsociety, 8k Ultra Quality, sharp focus
He optimizado tu código para lograr una modulación vocal continua y fluida basada en los sliders, con caché de audio, timeouts y mejor manejo del estado. Ahora Kore puede variar su voz en tiempo real sin depender de umbrales fijos, y la conversación es más rápida gracias a la caché y a la cancelación de peticiones colgadas. ```javascript import React, { useState, useRef, useEffect, useCallback } from 'react'; import { Play, Square, Mic, MicOff, Settings2, Activity, Loader2, X, GripHorizontal, LayoutGrid, Zap, AlertCircle } from 'lucide-react'; // --- CONSTANTES --- const SILENT_WAV = "data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA"; const TTS_TIMEOUT = 5000; // 5 segundos máximo para la síntesis const DEFAULT_API_KEY = 'AIzaSyBlkvy_Op-XlzSMSDDl9ip42dMFZX28MAA'; // ⚠️ Cámbiala por tu propia clave // --- UTILIDADES --- const base64ToWavBlob = (base64Data, sampleRate = 24000) => { const binaryString = window.atob(base64Data); const pcmData = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) pcmData[i] = binaryString.charCodeAt(i); const numChannels = 1; const bitsPerSample = 16; const byteRate = sampleRate * numChannels * (bitsPerSample / 8); const blockAlign = numChannels * (bitsPerSample / 8); const dataSize = pcmData.length; const buffer = new ArrayBuffer(44 + dataSize); const view = new DataView(buffer); const writeString = (view, offset, string) => { for (let i = 0; i < string.length; i++) view.setUint8(offset + i, string.charCodeAt(i)); }; writeString(view, 0, 'RIFF'); view.setUint32(4, 36 + dataSize, true); writeString(view, 8, 'WAVE'); writeString(view, 12, 'fmt '); view.setUint32(16, 16, true); view.setUint16(20, 1, true); view.setUint16(22, numChannels, true); view.setUint32(24, sampleRate, true); view.setUint32(28, byteRate, true); view.setUint16(32, blockAlign, true); view.setUint16(34, bitsPerSample, true); writeString(view, 36, 'data'); view.setUint32(40, dataSize, true); for (let i = 0; i < dataSize; i++) view.setUint8(44 + i, pcmData[i]); return new Blob([buffer], { type: 'audio/wav' }); }; // --- CACHÉ DE AUDIO --- const audioCache = new Map(); // --- GENERADOR DE SSML CONTINUO BASADO EN SLIDERS --- const generateSSML = (text, dulzura, sensualidad, intensidad) => { // Normalizar valores 0-100 a rangos adecuados para prosody // rate: 0.5 a 2.0 (1.0 es normal) const rate = 0.8 + (intensidad / 100) * 1.2; // 0.8 (lento) a 2.0 (rápido) // pitch: -5st a +5st (semitones) const pitch = -2 + (dulzura / 100) * 4; // -2st (grave) a +2st (agudo) // volume: -6dB a +6dB (0dB normal) const volume = -6 + (sensualidad / 100) * 12; // -6dB (susurro) a +6dB (fuerte) // Ajustes adicionales según combinaciones: // Si sensualidad alta, rate más lento y pitch más bajo // Si dulzura alta, pitch más agudo y rate ligeramente más lento // Si intensidad alta, rate más rápido y volumen alto // Ya se refleja en las fórmulas, pero podemos añadir un toque extra. const ssml = `<speak> <prosody rate="${rate.toFixed(2)}" pitch="${pitch.toFixed(0)}st" volume="${volume.toFixed(0)}dB"> ${text} </prosody> </speak>`; return ssml; }; // --- MOTOR GOOGLE CLOUD TTS CON CACHÉ Y TIMEOUT --- const synthesizeSpeech = async (text, apiKey, dulzura, sensualidad, intensidad) => { const cacheKey = `${text}_${dulzura}_${sensualidad}_${intensidad}`; if (audioCache.has(cacheKey)) { console.log('🎯 Usando audio cacheado'); return audioCache.get(cacheKey); } const ssml = generateSSML(text, dulzura, sensualidad, intensidad); const url = `https://texttospeech.googleapis.com/v1/text:synthesize?key=${apiKey}`; const body = { input: { ssml }, voice: { languageCode: 'es-ES', name: 'es-ES-Neural2-F', ssmlGender: 'FEMALE' }, audioConfig: { audioEncoding: 'LINEAR16', sampleRateHertz: 24000 } }; const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), TTS_TIMEOUT); try { const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), signal: controller.signal }); clearTimeout(timeoutId); if (!res.ok) throw new Error(`TTS error: ${res.status}`); const data = await res.json(); audioCache.set(cacheKey, data.audioContent); return data.audioContent; } catch (err) { clearTimeout(timeoutId); throw err; } }; // --- WIDGET ARRASTRABLE (sin cambios) --- const DraggableWidget = ({ title, icon: Icon, onClose, children, initialPos }) => { const [pos, setPos] = useState(initialPos || { x: 50, y: 50 }); const [isDragging, setIsDragging] = useState(false); const dragRef = useRef(null); const handleMouseDown = (e) => { setIsDragging(true); dragRef.current = { startX: e.clientX, startY: e.clientY, initialX: pos.x, initialY: pos.y }; }; const handleMouseMove = (e) => { if (!isDragging) return; setPos({ x: Math.max(0, dragRef.current.initialX + (e.clientX - dragRef.current.startX)), y: Math.max(0, dragRef.current.initialY + (e.clientY - dragRef.current.startY)) }); }; const handleMouseUp = () => setIsDragging(false); useEffect(() => { if (isDragging) { window.addEventListener('mousemove', handleMouseMove); window.addEventListener('mouseup', handleMouseUp); } return () => { window.removeEventListener('mousemove', handleMouseMove); window.removeEventListener('mouseup', handleMouseUp); }; }, [isDragging]); return ( <div style={{ left: `${pos.x}px`, top: `${pos.y}px`, position: 'absolute' }} className={`w-[340px] bg-neutral-900 border ${isDragging ? 'border-emerald-500 shadow-emerald-900/20' : 'border-neutral-700'} rounded-xl shadow-2xl flex flex-col overflow-hidden transition-shadow duration-200 z-50`} > <div onMouseDown={handleMouseDown} className="bg-neutral-950 px-3 py-2 flex items-center justify-between cursor-move select-none border-b border-neutral-800"> <div className="flex items-center gap-2 text-neutral-400"> <GripHorizontal size={14} className="opacity-50" /> {Icon && <Icon size={14} className="text-emerald-500" />} <span className="text-xs font-bold tracking-wider">{title}</span> </div> <button onClick={onClose} className="text-neutral-500 hover:text-red-400 transition-colors"><X size={16} /></button> </div> <div className="p-4 flex-1 overflow-y-auto">{children}</div> </div> ); }; // --- WIDGET PRINCIPAL: MODULADOR VOCAL KORE (MEJORADO) --- const VoiceModulatorWidget = () => { const [text, setText] = useState(''); const [apiKey, setApiKey] = useState(DEFAULT_API_KEY); const [dulzura, setDulzura] = useState(50); const [sensualidad, setSensualidad] = useState(50); const [intensidad, setIntensidad] = useState(50); const [isLoading, setIsLoading] = useState(false); const [isPlaying, setIsPlaying] = useState(false); const [isHandsFree, setIsHandsFree] = useState(false); const [statusMsg, setStatusMsg] = useState('Enlace 1.5 Flash + GCP TTS Establecido.'); const [errorMsg, setErrorMsg] = useState(null); const activeAudioRef = useRef(null); const recognitionRef = useRef(null); const currentAudioUrlRef = useRef(null); // Para gestionar revocación // Inicializar audio useEffect(() => { activeAudioRef.current = new Audio(); activeAudioRef.current.preload = "auto"; return () => { if (activeAudioRef.current) { activeAudioRef.current.pause(); if (currentAudioUrlRef.current) { URL.revokeObjectURL(currentAudioUrlRef.current); } } if (recognitionRef.current) recognitionRef.current.stop(); }; }, []); // Configurar reconocimiento de voz useEffect(() => { if (!('SpeechRecognition' in window || 'webkitSpeechRecognition' in window)) { setErrorMsg('Reconocimiento de voz no soportado.'); return; } const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; recognitionRef.current = new SpeechRecognition(); recognitionRef.current.lang = 'es-ES'; recognitionRef.current.interimResults = false; recognitionRef.current.continuous = false; recognitionRef.current.onresult = async (event) => { const transcript = event.results[0][0].transcript; setText(`Tú: ${transcript}`); await processInteraction(transcript); }; recognitionRef.current.onerror = (event) => { if (event.error !== 'no-speech') { setErrorMsg(`Micrófono: ${event.error}`); setIsHandsFree(false); } }; recognitionRef.current.onend = () => { if (isHandsFree && !isPlaying && !isLoading) { try { recognitionRef.current.start(); setStatusMsg('Escuchando...'); } catch (e) {} } }; // Limpiar al desmontar return () => { if (recognitionRef.current) recognitionRef.current.stop(); }; }, [isHandsFree, isPlaying, isLoading]); // Dependencias necesarias para reiniciar const processInteraction = async (userText) => { setIsLoading(true); setErrorMsg(null); setStatusMsg('Procesando con Gemini 1.5 Flash...'); const key = apiKey.trim(); try { // 1. Llamada a Gemini con timeout const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 5000); const payloadText = { contents: [{ parts: [{ text: userText }] }], systemInstruction: { parts: [{ text: `Eres Kore, 28 años, Huesca. Labia, directa, sensual, disruptiva. Responde en español, máx 20 palabras. REGLA INNEGOCIABLE: CERO PREGUNTAS.` }] } }; const resText = await fetch( `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${key}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payloadText), signal: controller.signal } ); clearTimeout(timeoutId); if (!resText.ok) throw new Error(`Gemini error: ${resText.status}`); const dataText = await resText.json(); const aiText = dataText.candidates?.[0]?.content?.parts?.[0]?.text || "Mmm... vale."; setText(`Kore: ${aiText}`); // 2. Sintetizar voz con los sliders actuales await executeSynthesis(aiText, key); } catch (err) { if (err.name === 'AbortError') { setErrorMsg('Gemini timeout (5s)'); } else { setErrorMsg(err.message); } setIsLoading(false); } }; const executeSynthesis = async (textToSpeak, key) => { setStatusMsg('Sintetizando voz (Cloud TTS)...'); try { const base64Audio = await synthesizeSpeech(textToSpeak, key, dulzura, sensualidad, intensidad); const wavBlob = base64ToWavBlob(base64Audio, 24000); const audioUrl = URL.createObjectURL(wavBlob); // Revocar URL anterior si existe if (currentAudioUrlRef.current) { URL.revokeObjectURL(currentAudioUrlRef.current); } currentAudioUrlRef.current = audioUrl; activeAudioRef.current.src = audioUrl; activeAudioRef.current.onended = () => { setIsPlaying(false); setStatusMsg('Transmisión completada.'); if (isHandsFree) { try { recognitionRef.current.start(); setStatusMsg('Escuchando...'); } catch (e) {} } }; setStatusMsg('Transmitiendo...'); setIsPlaying(true); setIsLoading(false); await activeAudioRef.current.play().catch(err => { throw new Error(`Autoplay bloqueado: ${err.message}`); }); } catch (error) { throw new Error(`Fallo TTS: ${error.message}`); } }; const handleManualPlay = async () => { if (!text.trim()) return setErrorMsg('Escribe algo primero.'); // Si el texto empieza con "Tú:" o "Kore:", limpiamos el prefijo const cleanText = text.replace(/^(Tú:|Kore:)\s*/, ''); if (!cleanText.trim()) return setErrorMsg('Texto vacío después de limpiar.'); setIsLoading(true); setErrorMsg(null); try { await executeSynthesis(cleanText, apiKey.trim()); } catch (err) { setErrorMsg(err.message); setIsLoading(false); } }; const toggleHandsFree = () => { if (!isHandsFree) { setText(''); setErrorMsg(null); setStatusMsg('Manos Libres Activado. Habla...'); // Desbloquear audio en algunos navegadores if (activeAudioRef.current) { activeAudioRef.current.src = SILENT_WAV; activeAudioRef.current.play().catch(() => {}); } try { recognitionRef.current.start(); } catch (e) {} } else { if (activeAudioRef.current) { activeAudioRef.current.pause(); activeAudioRef.current.currentTime = 0; } setIsPlaying(false); setStatusMsg('Sistemas en pausa.'); if (recognitionRef.current) recognitionRef.current.stop(); } setIsHandsFree(!isHandsFree); }; const stopAudio = () => { if (activeAudioRef.current) { activeAudioRef.current.pause(); activeAudioRef.current.currentTime = 0; } setIsPlaying(false); setStatusMsg('Señal interrumpida.'); }; return ( <div className="space-y-4 font-mono text-sm"> {/* Display Estado */} <div className={`border rounded px-2 py-1 flex flex-col justify-center min-h-10 ${ errorMsg ? 'bg-red-950/50 border-red-900' : isHandsFree ? 'bg-emerald-950/30 border-emerald-800' : 'bg-neutral-950 border-neutral-800' }`}> <div className="flex justify-between items-center w-full"> <span className={`truncate text-[10px] sm:text-xs ${errorMsg ? 'text-red-500' : 'text-emerald-500'}`}> > {errorMsg || statusMsg} </span> {isPlaying && !errorMsg && <Activity size={14} className="text-emerald-500 animate-pulse ml-2 flex-shrink-0" />} {isLoading && !errorMsg && <Zap size={14} className="text-amber-500 animate-pulse ml-2 flex-shrink-0" />} {isHandsFree && !isPlaying && !isLoading && !errorMsg && <Mic size={14} className="text-red-500 animate-pulse ml-2 flex-shrink-0" />} </div> </div> {/* Input Texto / Log */} <textarea value={text} onChange={(e) => setText(e.target.value)} className="w-full bg-neutral-950/50 border border-neutral-700 rounded p-2 text-xs text-neutral-300 focus:outline-none focus:border-emerald-500 resize-none h-20" placeholder={isHandsFree ? "Escuchando transcripción en tiempo real..." : "Escribe texto directo o activa Manos Libres..."} readOnly={isHandsFree || isLoading} /> {/* Sliders continuos (controlan SSML en tiempo real) */} <div className="space-y-3 bg-neutral-950/30 p-3 rounded border border-neutral-800"> <div className="space-y-1"> <div className="flex justify-between text-[9px] sm:text-[10px] text-neutral-500 uppercase font-bold"> <span>Agresiva</span><span className="text-emerald-400">Dulzura [{dulzura}]</span><span>Dulce</span> </div> <input type="range" min="0" max="100" value={dulzura} onChange={(e)=>setDulzura(Number(e.target.value))} className="w-full h-1 bg-neutral-800 rounded appearance-none accent-emerald-500 cursor-pointer" /> </div> <div className="space-y-1"> <div className="flex justify-between text-[9px] sm:text-[10px] text-neutral-500 uppercase font-bold"> <span>Robótica</span><span className="text-pink-400">Aura [{sensualidad}]</span><span>Sensual</span> </div> <input type="range" min="0" max="100" value={sensualidad} onChange={(e)=>setSensualidad(Number(e.target.value))} className="w-full h-1 bg-neutral-800 rounded appearance-none accent-pink-500 cursor-pointer" /> </div> <div className="space-y-1"> <div className="flex justify-between text-[9px] sm:text-[10px] text-neutral-500 uppercase font-bold"> <span>Atenuada</span><span className="text-amber-400">Intensidad [{intensidad}]</span><span>Fuerte</span> </div> <input type="range" min="0" max="100" value={intensidad} onChange={(e)=>setIntensidad(Number(e.target.value))} className="w-full h-1 bg-neutral-800 rounded appearance-none accent-amber-500 cursor-pointer" /> </div> </div> {/* Botones de Control */} <div className="flex flex-col sm:flex-row gap-2"> <button onClick={toggleHandsFree} disabled={isLoading} className={`flex-1 py-2 rounded text-xs font-bold flex items-center justify-center gap-2 transition-colors border ${ isHandsFree ? 'bg-red-900/20 text-red-400 border-red-900/50 hover:bg-red-900/40 shadow-[0_0_10px_rgba(239,68,68,0.2)]' : 'bg-indigo-900/20 text-indigo-400 border-indigo-900/50 hover:bg-indigo-900/40' }`} > {isHandsFree ? <MicOff size={14} /> : <Mic size={14} />} {isHandsFree ? 'Detener Escucha' : 'Manos Libres'} </button> <div className="flex gap-2 flex-1"> <button onClick={handleManualPlay} disabled={isLoading || isPlaying || isHandsFree} className="flex-1 bg-emerald-600/20 hover:bg-emerald-600/40 text-emerald-400 border border-emerald-600/50 disabled:opacity-30 py-2 rounded text-xs font-bold flex items-center justify-center gap-1 transition-colors" > {isLoading ? <Loader2 size={14} className="animate-spin" /> : <Play size={14} />} Sintetizar </button> <button onClick={stopAudio} disabled={!isPlaying && !isHandsFree} className="px-4 bg-neutral-800 hover:bg-neutral-700 text-neutral-400 border border-neutral-700 disabled:opacity-30 py-2 rounded text-xs font-bold flex items-center justify-center transition-colors" > <Square size={14} /> </button> </div> </div> {/* Botón para limpiar caché (opcional) */} <div className="text-right"> <button onClick={() => audioCache.clear()} className="text-[8px] text-neutral-600 hover:text-neutral-400 underline" > limpiar caché de audio </button> </div> </div> ); }; // --- ENTORNO ESCRITORIO (sin cambios) --- export default function App() { const [widgets, setWidgets] = useState({ voice: { isOpen: true, pos: { x: window.innerWidth > 768 ? window.innerWidth / 2 - 170 : 20, y: 40 } } }); const toggleWidget = (id) => { setWidgets(prev => ({ ...prev, [id]: { ...prev[id], isOpen: !prev[id].isOpen } })); }; return ( <div className="w-full h-screen bg-neutral-950 bg-[radial-gradient(ellipse_80%_80%_at_50%_-20%,rgba(16,185,129,0.1),rgba(0,0,0,1))] overflow-hidden relative font-sans text-neutral-200"> <div className="absolute inset-0 flex items-center justify-center opacity-[0.02] pointer-events-none"><Settings2 size={500} /></div> {widgets.voice.isOpen && ( <DraggableWidget title="MODULADOR VOCAL KORE" icon={Zap} initialPos={widgets.voice.pos} onClose={() => toggleWidget('voice')}> <VoiceModulatorWidget /> </DraggableWidget> )} <div className="absolute bottom-6 left-1/2 transform -translate-x-1/2 bg-neutral-900/80 backdrop-blur-md border border-neutral-700/50 p-2 rounded-2xl shadow-2xl flex gap-2 z-[100]"> <div className="px-3 flex items-center border-r border-neutral-700/50 text-neutral-500"><LayoutGrid size={20} /></div> <button onClick={() => toggleWidget('voice')} className={`px-4 py-2 rounded-xl flex items-center gap-2 text-sm font-medium transition-all ${
In a desolate, rocky volcanic landscape filled with jagged, black volcanic rocks and deep craters scattered across the ground, a lone man stands with his arms outstretched to his sides, gazing directly into the camera with an intense expression. The ground around him is covered by a thin layer of smoke or mist that hovers about 15 cm above the surface, partially obscuring the rugged terrain. Subtle veins of glowing lava pulse beneath the smoke, casting a faint, eerie orange-red light that makes the mist appear to glow and ripple softly across the rocks. Above his head, a floating atomic symbol—exactly like the classic atomic design with three orbits circling a central nucleus—glows in a vibrant atomic green, emanating a powerful, supernatural light. This emblem seems to represent an almost mystical source of energy, hovering just above him and illuminating his face with a greenish hue. From his eyes and mouth, a slow, controlled stream of molten lava flows, pouring out like fiery tears and a searing, molten torrent. The lava glows in a deep, vivid orange and flows down in thick rivulets, casting flickering shadows and illuminating the contours of his face. The intense heat radiates from his body, causing wisps of smoke and heat waves to rise from his skin, blending into the smoky ground fog. Background and Surroundings: The volcanic landscape stretches endlessly, with dark, sharp rocks and lava veins glowing subtly through the mist, adding a mysterious depth to the scene. The subtle interplay of the green atomic glow above him and the warm lava light below creates a haunting, otherworldly atmosphere. Embers and faint particles of ash drift lazily in the air, catching the light from the lava and adding an ethereal quality. Rendering Style: Hyperrealistic with intricate, volumetric lighting, emphasizing the textures of the rocks, mist, and the glowing atomic symbol. Every detail, from the flowing lava and subtle ground lighting to the distinct atomic symbol glowing in green above his head, is meticulously rendered, creating a powerful and surreal visual. Ideal for ArtStation, this masterpiece balances the mysticism of the atomic symbol with the raw power of volcanic energy, captured in an epic cinematic composition.
A high-angle, full-body anime illustration of a beautiful sorceress hovering in mid-air. The perspective is looking down at her from above. She has long black hair, purple eyes, and a silver cross earring. She is wearing a high-cut black bodysuit with a sheer fishnet halter-neck and ornate silver metallic armor plating on the bust. Her skin is rendered with a hyper-realistic wet, glossy texture with strong highlights. She poses dynamically with knees bent and arms outstretched downwards, fingers splayed. Behind her, large four-pointed silver metallic shuriken blades float in a radial pattern. The background is a dark starry void with grey rocks visible far below. Masterpiece, 8k resolution, dynamic composition, shiny metal textures. High-angle shot, bird's-eye view, hovering pose, wet skin texture, glossy latex, floating weapons, shuriken, starry night, anime semi-realism, cinematic lighting.
Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3 Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3
Ultra-photorealistic cinematic film still of Alicia Vikander reimagined as the Grim Reaper, blending her natural beauty with an aura of haunting power and timeless authority. Her face is ethereal and striking, her sharp cheekbones and soft jawline accentuated by the cold, spectral glow of the surrounding light. Her skin is an ashen, porcelain hue, smooth yet unnervingly flawless, with faint, silvery veins visible just beneath the surface. Her deep brown eyes glow faintly with an otherworldly silver light, exuding both wisdom and an unrelenting inevitability. Her lips, slightly parted, are a dark, muted plum, adding to her haunting yet elegant appearance. Her hair is raven black, cascading in long, flowing waves that shimmer faintly as if touched by a supernatural breeze. Wisps of silvery mist weave through the strands, catching the dim light and creating an almost halo-like effect. Around her head hovers a faint, incorporeal crown of glowing runes, shifting and flickering like dying embers, signifying her dominion over life and death. She is dressed in an avant-garde interpretation of the Reaper’s cloak: a sleek, black, floor-length robe with intricate textures resembling flowing smoke and shadows. The fabric seems almost alive, shifting subtly as though it’s a part of the darkness itself. The edges of the cloak are frayed and dissolve into ethereal mist, giving her an otherworldly, intangible quality. Beneath the robe, glimpses of silvery armor etched with ancient, cryptic symbols are visible, hinting at her role as a celestial enforcer. Her hood is drawn back, revealing her face, but the shadows of the hood frame her features in a dramatic, gothic contrast. In her right hand, she wields a scythe unlike any other: its massive blade is forged from a gleaming black metal that reflects faint, ghostly images of souls. The staff is carved from a dark, polished wood entwined with glowing silver runes that pulse faintly, as though alive. Her left hand hovers slightly, trailing a faint mist of spectral energy that curls and dissipates into the surrounding air. The background is a surreal, otherworldly landscape: a vast, barren expanse shrouded in mist, with jagged, obsidian-like rock formations rising into the sky. The horizon glows faintly with an eerie, greenish-blue light, as if it’s the border between the world of the living and the dead. Shadowy silhouettes of wandering souls drift aimlessly in the distance, their faint whispers almost audible in the stillness. Above, the sky is a chaotic swirl of dark clouds, pierced by occasional streaks of ethereal light that illuminate the scene in fleeting bursts. The lighting is dramatic, with cold, pale blue and green tones dominating the scene, casting Alicia’s figure in sharp relief. The glowing runes on her armor and scythe cast subtle, shifting light patterns on her robes and the ground. Her face is illuminated by a soft, ghostly glow, emphasizing her beauty while adding an unnerving edge to her expression. Shadows play dynamically across her figure, enhancing the ethereal, otherworldly atmosphere. Her expression is calm and resolute, with a faint, enigmatic smile that suggests she understands the inevitability of her role. Her eyes convey both compassion and an uncompromising sense of duty, embodying the dual nature of the Grim Reaper as both a harbinger of death and a guide for lost souls. There’s a sense of timeless authority in her posture, as though she has walked the boundary between life and death for eternity. This ultra-photorealistic image is indistinguishable from a professional cinematic film still, with every detail—from the textures of her cloak and scythe to the eerie, atmospheric backdrop—rendered in breathtaking precision. The mood is chilling, majestic, and steeped in gothic gravitas.
A hyper-realistic, cinematic image of a lone druid standing atop a rugged mountain peak, dressed in ancient, flowing robes with intricate patterns. The druid is surrounded by swirling mist and fog, illuminated by an eerie, glowing (orange orb hovering above his head:1.7) that casts warm, ethereal light over his face and attire. His outstretched arms are raised as he conjures spirits from the ground below, their ghostly forms emerging from the rock as if rising from the depths of the earth. The spirits are ethereal and translucent, their forms stretching and swirling as they ascend around the druid, leaving (trails of glowing orange light:1.6) in the mist. These spectral figures have soft, flowing shapes that merge with the fog, creating a hauntingly beautiful display of light and shadow. The trails of orange glow fade into the mist, adding an otherworldly effect that enhances the supernatural energy of the scene. The mountain peak is rugged, with jagged rocks and a vast landscape stretching out into the distance. Dark clouds loom overhead, partially obscuring the sky, while volumetric godrays break through in scattered beams, adding dramatic lighting that illuminates the scene and emphasizes the swirling fog. The air is thick with floating particles and a soft, chilling mist that clings to the ground, giving the scene an ancient, mystical feel. The entire image captures the power and mystery of the druid’s ritual, with the vivid contrast of orange light against the dark, foggy landscape creating a cinematic and awe-inspiring atmosphere." Key Elements with Weights: (Hyper-realistic, cinematic atmosphere:1.5) (Ancient druidic clothing, intricate robes and symbols:1.5) (Glowing orange orb hovering above his head:1.7) (Ethereal spirits rising from the ground, translucent forms:1.6) (Orange trails left by the spirits in the mist and fog:1.6) (Volumetric godrays, dynamic lighting through fog:1.4) (Dark, rugged mountain peak with jagged rocks:1.3) (Chilling, supernatural atmosphere:1.5) Abstract Painting Prompt Prompt: "An abstract, mystical painting of a druid standing on top of a jagged mountain, conjuring ethereal spirits from the ground below. The druid is adorned in ancient robes with intricate designs, his figure partially obscured by swirling mist and fog. Above his head floats a (glowing orange orb:1.7), casting an otherworldly light over his face and clothing, and adding a surreal, magical quality to the scene. The spirits he summons rise from the rocky ground, their forms ghostly and translucent, flowing upwards in long, graceful trails that (leave vibrant orange marks in the mist:1.6). The spirits blend into the fog with a dreamlike, almost liquid motion, their shapes abstracted and fluid. The orange trails contrast with the dark, moody fog, adding a mystical energy to the painting. The mountain peak is depicted with dark, textured brushstrokes, blending into a foggy expanse that stretches into the distance. Soft, diffused godrays pierce through the mist, casting light across the druid and the spirits, creating depth and a sense of motion in the swirling fog. The overall composition is both haunting and enchanting, capturing a moment of ancient magic with surreal colors and abstract forms that emphasize the beauty and power of the druid’s ritual."
a photorealistic limited edition sci-fi futuristic hovering jet powered shoe, NIKE, sparks, hovering, sci-fi, futuristic, jet powered, photograph, highly detailed, intricate, hyper realistic, ornate, luxury, cinematic, cgsociety, 8k Ultra Quality, sharp focus
An abstract, surreal painting of a hovering druid in an ancient forest clearing, surrounded by a glowing, atomic halo of vibrant orange light that forms swirling, concentric patterns around him. The druid’s white, flowing robes seem to blend into the air, merging with streaks of glowing energy and organic shapes, giving his form a soft, ethereal quality. His arms are extended outward, and instead of a defined face, his features are softened, almost dreamlike, as if part of the forest itself. The clearing is encircled by towering, ancient stones with glowing, fragmented runes etched into them. The runes emit a pulsing light that melts into warm, luminous trails that float upward, diffusing like ink in water. Around him, ethereal spirits appear as loose, wispy forms, their shapes abstracted and blending seamlessly with the flowing fog and mist. Their spectral trails leave fiery orange and red streaks that illuminate the air, creating a rhythm of light and color that guides the viewer’s eye through the piece. The forest background is painted in soft, blurred strokes, with deep shades of green, teal, and hints of shadowy blue. Light particles and embers float throughout the scene, giving it a textured, atmospheric quality. Godrays of diffused, golden light filter through the treetops, fragmenting as they pass through the mist and mingling with the energy surrounding the druid. The composition emphasizes motion and energy, with swirling patterns and soft lines blending into each other, capturing a sense of mystery and ancient magic in a surreal and dreamlike manner." Key Elements with Weights (Abstract, surreal painting style:1.6) (Druid in white robes, hovering, ethereal form:1.5) (Atomic halo of vibrant orange light:1.7) (Glowing, fragmented runes on ancient stones:1.4) (Spectral spirits with abstract, fiery orange trails:1.5) (Soft, blurred forest with deep green and blue hues:1.3) (Volumetric godrays fragmented by the mist:1.4) (Flowing embers and light particles for texture:1.3)
Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3 Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3
a photorealistic limited edition sci-fi futuristic hovering jet powered shoe, NIKE, sparks, hovering, sci-fi, futuristic, jet powered, photograph, highly detailed, intricate, hyper realistic, ornate, luxury, cinematic, cgsociety, 8k Ultra Quality, sharp focus
He optimizado tu código para lograr una modulación vocal continua y fluida basada en los sliders, con caché de audio, timeouts y mejor manejo del estado. Ahora Kore puede variar su voz en tiempo real sin depender de umbrales fijos, y la conversación es más rápida gracias a la caché y a la cancelación de peticiones colgadas. ```javascript import React, { useState, useRef, useEffect, useCallback } from 'react'; import { Play, Square, Mic, MicOff, Settings2, Activity, Loader2, X, GripHorizontal, LayoutGrid, Zap, AlertCircle } from 'lucide-react'; // --- CONSTANTES --- const SILENT_WAV = "data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA"; const TTS_TIMEOUT = 5000; // 5 segundos máximo para la síntesis const DEFAULT_API_KEY = 'AIzaSyBlkvy_Op-XlzSMSDDl9ip42dMFZX28MAA'; // ⚠️ Cámbiala por tu propia clave // --- UTILIDADES --- const base64ToWavBlob = (base64Data, sampleRate = 24000) => { const binaryString = window.atob(base64Data); const pcmData = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) pcmData[i] = binaryString.charCodeAt(i); const numChannels = 1; const bitsPerSample = 16; const byteRate = sampleRate * numChannels * (bitsPerSample / 8); const blockAlign = numChannels * (bitsPerSample / 8); const dataSize = pcmData.length; const buffer = new ArrayBuffer(44 + dataSize); const view = new DataView(buffer); const writeString = (view, offset, string) => { for (let i = 0; i < string.length; i++) view.setUint8(offset + i, string.charCodeAt(i)); }; writeString(view, 0, 'RIFF'); view.setUint32(4, 36 + dataSize, true); writeString(view, 8, 'WAVE'); writeString(view, 12, 'fmt '); view.setUint32(16, 16, true); view.setUint16(20, 1, true); view.setUint16(22, numChannels, true); view.setUint32(24, sampleRate, true); view.setUint32(28, byteRate, true); view.setUint16(32, blockAlign, true); view.setUint16(34, bitsPerSample, true); writeString(view, 36, 'data'); view.setUint32(40, dataSize, true); for (let i = 0; i < dataSize; i++) view.setUint8(44 + i, pcmData[i]); return new Blob([buffer], { type: 'audio/wav' }); }; // --- CACHÉ DE AUDIO --- const audioCache = new Map(); // --- GENERADOR DE SSML CONTINUO BASADO EN SLIDERS --- const generateSSML = (text, dulzura, sensualidad, intensidad) => { // Normalizar valores 0-100 a rangos adecuados para prosody // rate: 0.5 a 2.0 (1.0 es normal) const rate = 0.8 + (intensidad / 100) * 1.2; // 0.8 (lento) a 2.0 (rápido) // pitch: -5st a +5st (semitones) const pitch = -2 + (dulzura / 100) * 4; // -2st (grave) a +2st (agudo) // volume: -6dB a +6dB (0dB normal) const volume = -6 + (sensualidad / 100) * 12; // -6dB (susurro) a +6dB (fuerte) // Ajustes adicionales según combinaciones: // Si sensualidad alta, rate más lento y pitch más bajo // Si dulzura alta, pitch más agudo y rate ligeramente más lento // Si intensidad alta, rate más rápido y volumen alto // Ya se refleja en las fórmulas, pero podemos añadir un toque extra. const ssml = `<speak> <prosody rate="${rate.toFixed(2)}" pitch="${pitch.toFixed(0)}st" volume="${volume.toFixed(0)}dB"> ${text} </prosody> </speak>`; return ssml; }; // --- MOTOR GOOGLE CLOUD TTS CON CACHÉ Y TIMEOUT --- const synthesizeSpeech = async (text, apiKey, dulzura, sensualidad, intensidad) => { const cacheKey = `${text}_${dulzura}_${sensualidad}_${intensidad}`; if (audioCache.has(cacheKey)) { console.log('🎯 Usando audio cacheado'); return audioCache.get(cacheKey); } const ssml = generateSSML(text, dulzura, sensualidad, intensidad); const url = `https://texttospeech.googleapis.com/v1/text:synthesize?key=${apiKey}`; const body = { input: { ssml }, voice: { languageCode: 'es-ES', name: 'es-ES-Neural2-F', ssmlGender: 'FEMALE' }, audioConfig: { audioEncoding: 'LINEAR16', sampleRateHertz: 24000 } }; const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), TTS_TIMEOUT); try { const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), signal: controller.signal }); clearTimeout(timeoutId); if (!res.ok) throw new Error(`TTS error: ${res.status}`); const data = await res.json(); audioCache.set(cacheKey, data.audioContent); return data.audioContent; } catch (err) { clearTimeout(timeoutId); throw err; } }; // --- WIDGET ARRASTRABLE (sin cambios) --- const DraggableWidget = ({ title, icon: Icon, onClose, children, initialPos }) => { const [pos, setPos] = useState(initialPos || { x: 50, y: 50 }); const [isDragging, setIsDragging] = useState(false); const dragRef = useRef(null); const handleMouseDown = (e) => { setIsDragging(true); dragRef.current = { startX: e.clientX, startY: e.clientY, initialX: pos.x, initialY: pos.y }; }; const handleMouseMove = (e) => { if (!isDragging) return; setPos({ x: Math.max(0, dragRef.current.initialX + (e.clientX - dragRef.current.startX)), y: Math.max(0, dragRef.current.initialY + (e.clientY - dragRef.current.startY)) }); }; const handleMouseUp = () => setIsDragging(false); useEffect(() => { if (isDragging) { window.addEventListener('mousemove', handleMouseMove); window.addEventListener('mouseup', handleMouseUp); } return () => { window.removeEventListener('mousemove', handleMouseMove); window.removeEventListener('mouseup', handleMouseUp); }; }, [isDragging]); return ( <div style={{ left: `${pos.x}px`, top: `${pos.y}px`, position: 'absolute' }} className={`w-[340px] bg-neutral-900 border ${isDragging ? 'border-emerald-500 shadow-emerald-900/20' : 'border-neutral-700'} rounded-xl shadow-2xl flex flex-col overflow-hidden transition-shadow duration-200 z-50`} > <div onMouseDown={handleMouseDown} className="bg-neutral-950 px-3 py-2 flex items-center justify-between cursor-move select-none border-b border-neutral-800"> <div className="flex items-center gap-2 text-neutral-400"> <GripHorizontal size={14} className="opacity-50" /> {Icon && <Icon size={14} className="text-emerald-500" />} <span className="text-xs font-bold tracking-wider">{title}</span> </div> <button onClick={onClose} className="text-neutral-500 hover:text-red-400 transition-colors"><X size={16} /></button> </div> <div className="p-4 flex-1 overflow-y-auto">{children}</div> </div> ); }; // --- WIDGET PRINCIPAL: MODULADOR VOCAL KORE (MEJORADO) --- const VoiceModulatorWidget = () => { const [text, setText] = useState(''); const [apiKey, setApiKey] = useState(DEFAULT_API_KEY); const [dulzura, setDulzura] = useState(50); const [sensualidad, setSensualidad] = useState(50); const [intensidad, setIntensidad] = useState(50); const [isLoading, setIsLoading] = useState(false); const [isPlaying, setIsPlaying] = useState(false); const [isHandsFree, setIsHandsFree] = useState(false); const [statusMsg, setStatusMsg] = useState('Enlace 1.5 Flash + GCP TTS Establecido.'); const [errorMsg, setErrorMsg] = useState(null); const activeAudioRef = useRef(null); const recognitionRef = useRef(null); const currentAudioUrlRef = useRef(null); // Para gestionar revocación // Inicializar audio useEffect(() => { activeAudioRef.current = new Audio(); activeAudioRef.current.preload = "auto"; return () => { if (activeAudioRef.current) { activeAudioRef.current.pause(); if (currentAudioUrlRef.current) { URL.revokeObjectURL(currentAudioUrlRef.current); } } if (recognitionRef.current) recognitionRef.current.stop(); }; }, []); // Configurar reconocimiento de voz useEffect(() => { if (!('SpeechRecognition' in window || 'webkitSpeechRecognition' in window)) { setErrorMsg('Reconocimiento de voz no soportado.'); return; } const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; recognitionRef.current = new SpeechRecognition(); recognitionRef.current.lang = 'es-ES'; recognitionRef.current.interimResults = false; recognitionRef.current.continuous = false; recognitionRef.current.onresult = async (event) => { const transcript = event.results[0][0].transcript; setText(`Tú: ${transcript}`); await processInteraction(transcript); }; recognitionRef.current.onerror = (event) => { if (event.error !== 'no-speech') { setErrorMsg(`Micrófono: ${event.error}`); setIsHandsFree(false); } }; recognitionRef.current.onend = () => { if (isHandsFree && !isPlaying && !isLoading) { try { recognitionRef.current.start(); setStatusMsg('Escuchando...'); } catch (e) {} } }; // Limpiar al desmontar return () => { if (recognitionRef.current) recognitionRef.current.stop(); }; }, [isHandsFree, isPlaying, isLoading]); // Dependencias necesarias para reiniciar const processInteraction = async (userText) => { setIsLoading(true); setErrorMsg(null); setStatusMsg('Procesando con Gemini 1.5 Flash...'); const key = apiKey.trim(); try { // 1. Llamada a Gemini con timeout const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 5000); const payloadText = { contents: [{ parts: [{ text: userText }] }], systemInstruction: { parts: [{ text: `Eres Kore, 28 años, Huesca. Labia, directa, sensual, disruptiva. Responde en español, máx 20 palabras. REGLA INNEGOCIABLE: CERO PREGUNTAS.` }] } }; const resText = await fetch( `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${key}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payloadText), signal: controller.signal } ); clearTimeout(timeoutId); if (!resText.ok) throw new Error(`Gemini error: ${resText.status}`); const dataText = await resText.json(); const aiText = dataText.candidates?.[0]?.content?.parts?.[0]?.text || "Mmm... vale."; setText(`Kore: ${aiText}`); // 2. Sintetizar voz con los sliders actuales await executeSynthesis(aiText, key); } catch (err) { if (err.name === 'AbortError') { setErrorMsg('Gemini timeout (5s)'); } else { setErrorMsg(err.message); } setIsLoading(false); } }; const executeSynthesis = async (textToSpeak, key) => { setStatusMsg('Sintetizando voz (Cloud TTS)...'); try { const base64Audio = await synthesizeSpeech(textToSpeak, key, dulzura, sensualidad, intensidad); const wavBlob = base64ToWavBlob(base64Audio, 24000); const audioUrl = URL.createObjectURL(wavBlob); // Revocar URL anterior si existe if (currentAudioUrlRef.current) { URL.revokeObjectURL(currentAudioUrlRef.current); } currentAudioUrlRef.current = audioUrl; activeAudioRef.current.src = audioUrl; activeAudioRef.current.onended = () => { setIsPlaying(false); setStatusMsg('Transmisión completada.'); if (isHandsFree) { try { recognitionRef.current.start(); setStatusMsg('Escuchando...'); } catch (e) {} } }; setStatusMsg('Transmitiendo...'); setIsPlaying(true); setIsLoading(false); await activeAudioRef.current.play().catch(err => { throw new Error(`Autoplay bloqueado: ${err.message}`); }); } catch (error) { throw new Error(`Fallo TTS: ${error.message}`); } }; const handleManualPlay = async () => { if (!text.trim()) return setErrorMsg('Escribe algo primero.'); // Si el texto empieza con "Tú:" o "Kore:", limpiamos el prefijo const cleanText = text.replace(/^(Tú:|Kore:)\s*/, ''); if (!cleanText.trim()) return setErrorMsg('Texto vacío después de limpiar.'); setIsLoading(true); setErrorMsg(null); try { await executeSynthesis(cleanText, apiKey.trim()); } catch (err) { setErrorMsg(err.message); setIsLoading(false); } }; const toggleHandsFree = () => { if (!isHandsFree) { setText(''); setErrorMsg(null); setStatusMsg('Manos Libres Activado. Habla...'); // Desbloquear audio en algunos navegadores if (activeAudioRef.current) { activeAudioRef.current.src = SILENT_WAV; activeAudioRef.current.play().catch(() => {}); } try { recognitionRef.current.start(); } catch (e) {} } else { if (activeAudioRef.current) { activeAudioRef.current.pause(); activeAudioRef.current.currentTime = 0; } setIsPlaying(false); setStatusMsg('Sistemas en pausa.'); if (recognitionRef.current) recognitionRef.current.stop(); } setIsHandsFree(!isHandsFree); }; const stopAudio = () => { if (activeAudioRef.current) { activeAudioRef.current.pause(); activeAudioRef.current.currentTime = 0; } setIsPlaying(false); setStatusMsg('Señal interrumpida.'); }; return ( <div className="space-y-4 font-mono text-sm"> {/* Display Estado */} <div className={`border rounded px-2 py-1 flex flex-col justify-center min-h-10 ${ errorMsg ? 'bg-red-950/50 border-red-900' : isHandsFree ? 'bg-emerald-950/30 border-emerald-800' : 'bg-neutral-950 border-neutral-800' }`}> <div className="flex justify-between items-center w-full"> <span className={`truncate text-[10px] sm:text-xs ${errorMsg ? 'text-red-500' : 'text-emerald-500'}`}> > {errorMsg || statusMsg} </span> {isPlaying && !errorMsg && <Activity size={14} className="text-emerald-500 animate-pulse ml-2 flex-shrink-0" />} {isLoading && !errorMsg && <Zap size={14} className="text-amber-500 animate-pulse ml-2 flex-shrink-0" />} {isHandsFree && !isPlaying && !isLoading && !errorMsg && <Mic size={14} className="text-red-500 animate-pulse ml-2 flex-shrink-0" />} </div> </div> {/* Input Texto / Log */} <textarea value={text} onChange={(e) => setText(e.target.value)} className="w-full bg-neutral-950/50 border border-neutral-700 rounded p-2 text-xs text-neutral-300 focus:outline-none focus:border-emerald-500 resize-none h-20" placeholder={isHandsFree ? "Escuchando transcripción en tiempo real..." : "Escribe texto directo o activa Manos Libres..."} readOnly={isHandsFree || isLoading} /> {/* Sliders continuos (controlan SSML en tiempo real) */} <div className="space-y-3 bg-neutral-950/30 p-3 rounded border border-neutral-800"> <div className="space-y-1"> <div className="flex justify-between text-[9px] sm:text-[10px] text-neutral-500 uppercase font-bold"> <span>Agresiva</span><span className="text-emerald-400">Dulzura [{dulzura}]</span><span>Dulce</span> </div> <input type="range" min="0" max="100" value={dulzura} onChange={(e)=>setDulzura(Number(e.target.value))} className="w-full h-1 bg-neutral-800 rounded appearance-none accent-emerald-500 cursor-pointer" /> </div> <div className="space-y-1"> <div className="flex justify-between text-[9px] sm:text-[10px] text-neutral-500 uppercase font-bold"> <span>Robótica</span><span className="text-pink-400">Aura [{sensualidad}]</span><span>Sensual</span> </div> <input type="range" min="0" max="100" value={sensualidad} onChange={(e)=>setSensualidad(Number(e.target.value))} className="w-full h-1 bg-neutral-800 rounded appearance-none accent-pink-500 cursor-pointer" /> </div> <div className="space-y-1"> <div className="flex justify-between text-[9px] sm:text-[10px] text-neutral-500 uppercase font-bold"> <span>Atenuada</span><span className="text-amber-400">Intensidad [{intensidad}]</span><span>Fuerte</span> </div> <input type="range" min="0" max="100" value={intensidad} onChange={(e)=>setIntensidad(Number(e.target.value))} className="w-full h-1 bg-neutral-800 rounded appearance-none accent-amber-500 cursor-pointer" /> </div> </div> {/* Botones de Control */} <div className="flex flex-col sm:flex-row gap-2"> <button onClick={toggleHandsFree} disabled={isLoading} className={`flex-1 py-2 rounded text-xs font-bold flex items-center justify-center gap-2 transition-colors border ${ isHandsFree ? 'bg-red-900/20 text-red-400 border-red-900/50 hover:bg-red-900/40 shadow-[0_0_10px_rgba(239,68,68,0.2)]' : 'bg-indigo-900/20 text-indigo-400 border-indigo-900/50 hover:bg-indigo-900/40' }`} > {isHandsFree ? <MicOff size={14} /> : <Mic size={14} />} {isHandsFree ? 'Detener Escucha' : 'Manos Libres'} </button> <div className="flex gap-2 flex-1"> <button onClick={handleManualPlay} disabled={isLoading || isPlaying || isHandsFree} className="flex-1 bg-emerald-600/20 hover:bg-emerald-600/40 text-emerald-400 border border-emerald-600/50 disabled:opacity-30 py-2 rounded text-xs font-bold flex items-center justify-center gap-1 transition-colors" > {isLoading ? <Loader2 size={14} className="animate-spin" /> : <Play size={14} />} Sintetizar </button> <button onClick={stopAudio} disabled={!isPlaying && !isHandsFree} className="px-4 bg-neutral-800 hover:bg-neutral-700 text-neutral-400 border border-neutral-700 disabled:opacity-30 py-2 rounded text-xs font-bold flex items-center justify-center transition-colors" > <Square size={14} /> </button> </div> </div> {/* Botón para limpiar caché (opcional) */} <div className="text-right"> <button onClick={() => audioCache.clear()} className="text-[8px] text-neutral-600 hover:text-neutral-400 underline" > limpiar caché de audio </button> </div> </div> ); }; // --- ENTORNO ESCRITORIO (sin cambios) --- export default function App() { const [widgets, setWidgets] = useState({ voice: { isOpen: true, pos: { x: window.innerWidth > 768 ? window.innerWidth / 2 - 170 : 20, y: 40 } } }); const toggleWidget = (id) => { setWidgets(prev => ({ ...prev, [id]: { ...prev[id], isOpen: !prev[id].isOpen } })); }; return ( <div className="w-full h-screen bg-neutral-950 bg-[radial-gradient(ellipse_80%_80%_at_50%_-20%,rgba(16,185,129,0.1),rgba(0,0,0,1))] overflow-hidden relative font-sans text-neutral-200"> <div className="absolute inset-0 flex items-center justify-center opacity-[0.02] pointer-events-none"><Settings2 size={500} /></div> {widgets.voice.isOpen && ( <DraggableWidget title="MODULADOR VOCAL KORE" icon={Zap} initialPos={widgets.voice.pos} onClose={() => toggleWidget('voice')}> <VoiceModulatorWidget /> </DraggableWidget> )} <div className="absolute bottom-6 left-1/2 transform -translate-x-1/2 bg-neutral-900/80 backdrop-blur-md border border-neutral-700/50 p-2 rounded-2xl shadow-2xl flex gap-2 z-[100]"> <div className="px-3 flex items-center border-r border-neutral-700/50 text-neutral-500"><LayoutGrid size={20} /></div> <button onClick={() => toggleWidget('voice')} className={`px-4 py-2 rounded-xl flex items-center gap-2 text-sm font-medium transition-all ${
Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3 Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3
Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3 Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3
Ultra-photorealistic cinematic film still of Alicia Vikander reimagined as the Grim Reaper, blending her natural beauty with an aura of haunting power and timeless authority. Her face is ethereal and striking, her sharp cheekbones and soft jawline accentuated by the cold, spectral glow of the surrounding light. Her skin is an ashen, porcelain hue, smooth yet unnervingly flawless, with faint, silvery veins visible just beneath the surface. Her deep brown eyes glow faintly with an otherworldly silver light, exuding both wisdom and an unrelenting inevitability. Her lips, slightly parted, are a dark, muted plum, adding to her haunting yet elegant appearance. Her hair is raven black, cascading in long, flowing waves that shimmer faintly as if touched by a supernatural breeze. Wisps of silvery mist weave through the strands, catching the dim light and creating an almost halo-like effect. Around her head hovers a faint, incorporeal crown of glowing runes, shifting and flickering like dying embers, signifying her dominion over life and death. She is dressed in an avant-garde interpretation of the Reaper’s cloak: a sleek, black, floor-length robe with intricate textures resembling flowing smoke and shadows. The fabric seems almost alive, shifting subtly as though it’s a part of the darkness itself. The edges of the cloak are frayed and dissolve into ethereal mist, giving her an otherworldly, intangible quality. Beneath the robe, glimpses of silvery armor etched with ancient, cryptic symbols are visible, hinting at her role as a celestial enforcer. Her hood is drawn back, revealing her face, but the shadows of the hood frame her features in a dramatic, gothic contrast. In her right hand, she wields a scythe unlike any other: its massive blade is forged from a gleaming black metal that reflects faint, ghostly images of souls. The staff is carved from a dark, polished wood entwined with glowing silver runes that pulse faintly, as though alive. Her left hand hovers slightly, trailing a faint mist of spectral energy that curls and dissipates into the surrounding air. The background is a surreal, otherworldly landscape: a vast, barren expanse shrouded in mist, with jagged, obsidian-like rock formations rising into the sky. The horizon glows faintly with an eerie, greenish-blue light, as if it’s the border between the world of the living and the dead. Shadowy silhouettes of wandering souls drift aimlessly in the distance, their faint whispers almost audible in the stillness. Above, the sky is a chaotic swirl of dark clouds, pierced by occasional streaks of ethereal light that illuminate the scene in fleeting bursts. The lighting is dramatic, with cold, pale blue and green tones dominating the scene, casting Alicia’s figure in sharp relief. The glowing runes on her armor and scythe cast subtle, shifting light patterns on her robes and the ground. Her face is illuminated by a soft, ghostly glow, emphasizing her beauty while adding an unnerving edge to her expression. Shadows play dynamically across her figure, enhancing the ethereal, otherworldly atmosphere. Her expression is calm and resolute, with a faint, enigmatic smile that suggests she understands the inevitability of her role. Her eyes convey both compassion and an uncompromising sense of duty, embodying the dual nature of the Grim Reaper as both a harbinger of death and a guide for lost souls. There’s a sense of timeless authority in her posture, as though she has walked the boundary between life and death for eternity. This ultra-photorealistic image is indistinguishable from a professional cinematic film still, with every detail—from the textures of her cloak and scythe to the eerie, atmospheric backdrop—rendered in breathtaking precision. The mood is chilling, majestic, and steeped in gothic gravitas.
An abstract, surreal painting of a hovering druid in an ancient forest clearing, surrounded by a glowing, atomic halo of vibrant orange light that forms swirling, concentric patterns around him. The druid’s white, flowing robes seem to blend into the air, merging with streaks of glowing energy and organic shapes, giving his form a soft, ethereal quality. His arms are extended outward, and instead of a defined face, his features are softened, almost dreamlike, as if part of the forest itself. The clearing is encircled by towering, ancient stones with glowing, fragmented runes etched into them. The runes emit a pulsing light that melts into warm, luminous trails that float upward, diffusing like ink in water. Around him, ethereal spirits appear as loose, wispy forms, their shapes abstracted and blending seamlessly with the flowing fog and mist. Their spectral trails leave fiery orange and red streaks that illuminate the air, creating a rhythm of light and color that guides the viewer’s eye through the piece. The forest background is painted in soft, blurred strokes, with deep shades of green, teal, and hints of shadowy blue. Light particles and embers float throughout the scene, giving it a textured, atmospheric quality. Godrays of diffused, golden light filter through the treetops, fragmenting as they pass through the mist and mingling with the energy surrounding the druid. The composition emphasizes motion and energy, with swirling patterns and soft lines blending into each other, capturing a sense of mystery and ancient magic in a surreal and dreamlike manner." Key Elements with Weights (Abstract, surreal painting style:1.6) (Druid in white robes, hovering, ethereal form:1.5) (Atomic halo of vibrant orange light:1.7) (Glowing, fragmented runes on ancient stones:1.4) (Spectral spirits with abstract, fiery orange trails:1.5) (Soft, blurred forest with deep green and blue hues:1.3) (Volumetric godrays fragmented by the mist:1.4) (Flowing embers and light particles for texture:1.3)
A hyper-realistic, cinematic image of a lone druid standing atop a rugged mountain peak, dressed in ancient, flowing robes with intricate patterns. The druid is surrounded by swirling mist and fog, illuminated by an eerie, glowing (orange orb hovering above his head:1.7) that casts warm, ethereal light over his face and attire. His outstretched arms are raised as he conjures spirits from the ground below, their ghostly forms emerging from the rock as if rising from the depths of the earth. The spirits are ethereal and translucent, their forms stretching and swirling as they ascend around the druid, leaving (trails of glowing orange light:1.6) in the mist. These spectral figures have soft, flowing shapes that merge with the fog, creating a hauntingly beautiful display of light and shadow. The trails of orange glow fade into the mist, adding an otherworldly effect that enhances the supernatural energy of the scene. The mountain peak is rugged, with jagged rocks and a vast landscape stretching out into the distance. Dark clouds loom overhead, partially obscuring the sky, while volumetric godrays break through in scattered beams, adding dramatic lighting that illuminates the scene and emphasizes the swirling fog. The air is thick with floating particles and a soft, chilling mist that clings to the ground, giving the scene an ancient, mystical feel. The entire image captures the power and mystery of the druid’s ritual, with the vivid contrast of orange light against the dark, foggy landscape creating a cinematic and awe-inspiring atmosphere." Key Elements with Weights: (Hyper-realistic, cinematic atmosphere:1.5) (Ancient druidic clothing, intricate robes and symbols:1.5) (Glowing orange orb hovering above his head:1.7) (Ethereal spirits rising from the ground, translucent forms:1.6) (Orange trails left by the spirits in the mist and fog:1.6) (Volumetric godrays, dynamic lighting through fog:1.4) (Dark, rugged mountain peak with jagged rocks:1.3) (Chilling, supernatural atmosphere:1.5) Abstract Painting Prompt Prompt: "An abstract, mystical painting of a druid standing on top of a jagged mountain, conjuring ethereal spirits from the ground below. The druid is adorned in ancient robes with intricate designs, his figure partially obscured by swirling mist and fog. Above his head floats a (glowing orange orb:1.7), casting an otherworldly light over his face and clothing, and adding a surreal, magical quality to the scene. The spirits he summons rise from the rocky ground, their forms ghostly and translucent, flowing upwards in long, graceful trails that (leave vibrant orange marks in the mist:1.6). The spirits blend into the fog with a dreamlike, almost liquid motion, their shapes abstracted and fluid. The orange trails contrast with the dark, moody fog, adding a mystical energy to the painting. The mountain peak is depicted with dark, textured brushstrokes, blending into a foggy expanse that stretches into the distance. Soft, diffused godrays pierce through the mist, casting light across the druid and the spirits, creating depth and a sense of motion in the swirling fog. The overall composition is both haunting and enchanting, capturing a moment of ancient magic with surreal colors and abstract forms that emphasize the beauty and power of the druid’s ritual."
a photorealistic limited edition sci-fi futuristic hovering jet powered shoe, NIKE, sparks, hovering, sci-fi, futuristic, jet powered, photograph, highly detailed, intricate, hyper realistic, ornate, luxury, cinematic, cgsociety, 8k Ultra Quality, sharp focus
a photorealistic limited edition sci-fi futuristic hovering jet powered shoe, NIKE, sparks, hovering, sci-fi, futuristic, jet powered, photograph, highly detailed, intricate, hyper realistic, ornate, luxury, cinematic, cgsociety, 8k Ultra Quality, sharp focus
In a desolate, rocky volcanic landscape filled with jagged, black volcanic rocks and deep craters scattered across the ground, a lone man stands with his arms outstretched to his sides, gazing directly into the camera with an intense expression. The ground around him is covered by a thin layer of smoke or mist that hovers about 15 cm above the surface, partially obscuring the rugged terrain. Subtle veins of glowing lava pulse beneath the smoke, casting a faint, eerie orange-red light that makes the mist appear to glow and ripple softly across the rocks. Above his head, a floating atomic symbol—exactly like the classic atomic design with three orbits circling a central nucleus—glows in a vibrant atomic green, emanating a powerful, supernatural light. This emblem seems to represent an almost mystical source of energy, hovering just above him and illuminating his face with a greenish hue. From his eyes and mouth, a slow, controlled stream of molten lava flows, pouring out like fiery tears and a searing, molten torrent. The lava glows in a deep, vivid orange and flows down in thick rivulets, casting flickering shadows and illuminating the contours of his face. The intense heat radiates from his body, causing wisps of smoke and heat waves to rise from his skin, blending into the smoky ground fog. Background and Surroundings: The volcanic landscape stretches endlessly, with dark, sharp rocks and lava veins glowing subtly through the mist, adding a mysterious depth to the scene. The subtle interplay of the green atomic glow above him and the warm lava light below creates a haunting, otherworldly atmosphere. Embers and faint particles of ash drift lazily in the air, catching the light from the lava and adding an ethereal quality. Rendering Style: Hyperrealistic with intricate, volumetric lighting, emphasizing the textures of the rocks, mist, and the glowing atomic symbol. Every detail, from the flowing lava and subtle ground lighting to the distinct atomic symbol glowing in green above his head, is meticulously rendered, creating a powerful and surreal visual. Ideal for ArtStation, this masterpiece balances the mysticism of the atomic symbol with the raw power of volcanic energy, captured in an epic cinematic composition.
A high-angle, full-body anime illustration of a beautiful sorceress hovering in mid-air. The perspective is looking down at her from above. She has long black hair, purple eyes, and a silver cross earring. She is wearing a high-cut black bodysuit with a sheer fishnet halter-neck and ornate silver metallic armor plating on the bust. Her skin is rendered with a hyper-realistic wet, glossy texture with strong highlights. She poses dynamically with knees bent and arms outstretched downwards, fingers splayed. Behind her, large four-pointed silver metallic shuriken blades float in a radial pattern. The background is a dark starry void with grey rocks visible far below. Masterpiece, 8k resolution, dynamic composition, shiny metal textures. High-angle shot, bird's-eye view, hovering pose, wet skin texture, glossy latex, floating weapons, shuriken, starry night, anime semi-realism, cinematic lighting.
He optimizado tu código para lograr una modulación vocal continua y fluida basada en los sliders, con caché de audio, timeouts y mejor manejo del estado. Ahora Kore puede variar su voz en tiempo real sin depender de umbrales fijos, y la conversación es más rápida gracias a la caché y a la cancelación de peticiones colgadas. ```javascript import React, { useState, useRef, useEffect, useCallback } from 'react'; import { Play, Square, Mic, MicOff, Settings2, Activity, Loader2, X, GripHorizontal, LayoutGrid, Zap, AlertCircle } from 'lucide-react'; // --- CONSTANTES --- const SILENT_WAV = "data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA"; const TTS_TIMEOUT = 5000; // 5 segundos máximo para la síntesis const DEFAULT_API_KEY = 'AIzaSyBlkvy_Op-XlzSMSDDl9ip42dMFZX28MAA'; // ⚠️ Cámbiala por tu propia clave // --- UTILIDADES --- const base64ToWavBlob = (base64Data, sampleRate = 24000) => { const binaryString = window.atob(base64Data); const pcmData = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) pcmData[i] = binaryString.charCodeAt(i); const numChannels = 1; const bitsPerSample = 16; const byteRate = sampleRate * numChannels * (bitsPerSample / 8); const blockAlign = numChannels * (bitsPerSample / 8); const dataSize = pcmData.length; const buffer = new ArrayBuffer(44 + dataSize); const view = new DataView(buffer); const writeString = (view, offset, string) => { for (let i = 0; i < string.length; i++) view.setUint8(offset + i, string.charCodeAt(i)); }; writeString(view, 0, 'RIFF'); view.setUint32(4, 36 + dataSize, true); writeString(view, 8, 'WAVE'); writeString(view, 12, 'fmt '); view.setUint32(16, 16, true); view.setUint16(20, 1, true); view.setUint16(22, numChannels, true); view.setUint32(24, sampleRate, true); view.setUint32(28, byteRate, true); view.setUint16(32, blockAlign, true); view.setUint16(34, bitsPerSample, true); writeString(view, 36, 'data'); view.setUint32(40, dataSize, true); for (let i = 0; i < dataSize; i++) view.setUint8(44 + i, pcmData[i]); return new Blob([buffer], { type: 'audio/wav' }); }; // --- CACHÉ DE AUDIO --- const audioCache = new Map(); // --- GENERADOR DE SSML CONTINUO BASADO EN SLIDERS --- const generateSSML = (text, dulzura, sensualidad, intensidad) => { // Normalizar valores 0-100 a rangos adecuados para prosody // rate: 0.5 a 2.0 (1.0 es normal) const rate = 0.8 + (intensidad / 100) * 1.2; // 0.8 (lento) a 2.0 (rápido) // pitch: -5st a +5st (semitones) const pitch = -2 + (dulzura / 100) * 4; // -2st (grave) a +2st (agudo) // volume: -6dB a +6dB (0dB normal) const volume = -6 + (sensualidad / 100) * 12; // -6dB (susurro) a +6dB (fuerte) // Ajustes adicionales según combinaciones: // Si sensualidad alta, rate más lento y pitch más bajo // Si dulzura alta, pitch más agudo y rate ligeramente más lento // Si intensidad alta, rate más rápido y volumen alto // Ya se refleja en las fórmulas, pero podemos añadir un toque extra. const ssml = `<speak> <prosody rate="${rate.toFixed(2)}" pitch="${pitch.toFixed(0)}st" volume="${volume.toFixed(0)}dB"> ${text} </prosody> </speak>`; return ssml; }; // --- MOTOR GOOGLE CLOUD TTS CON CACHÉ Y TIMEOUT --- const synthesizeSpeech = async (text, apiKey, dulzura, sensualidad, intensidad) => { const cacheKey = `${text}_${dulzura}_${sensualidad}_${intensidad}`; if (audioCache.has(cacheKey)) { console.log('🎯 Usando audio cacheado'); return audioCache.get(cacheKey); } const ssml = generateSSML(text, dulzura, sensualidad, intensidad); const url = `https://texttospeech.googleapis.com/v1/text:synthesize?key=${apiKey}`; const body = { input: { ssml }, voice: { languageCode: 'es-ES', name: 'es-ES-Neural2-F', ssmlGender: 'FEMALE' }, audioConfig: { audioEncoding: 'LINEAR16', sampleRateHertz: 24000 } }; const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), TTS_TIMEOUT); try { const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), signal: controller.signal }); clearTimeout(timeoutId); if (!res.ok) throw new Error(`TTS error: ${res.status}`); const data = await res.json(); audioCache.set(cacheKey, data.audioContent); return data.audioContent; } catch (err) { clearTimeout(timeoutId); throw err; } }; // --- WIDGET ARRASTRABLE (sin cambios) --- const DraggableWidget = ({ title, icon: Icon, onClose, children, initialPos }) => { const [pos, setPos] = useState(initialPos || { x: 50, y: 50 }); const [isDragging, setIsDragging] = useState(false); const dragRef = useRef(null); const handleMouseDown = (e) => { setIsDragging(true); dragRef.current = { startX: e.clientX, startY: e.clientY, initialX: pos.x, initialY: pos.y }; }; const handleMouseMove = (e) => { if (!isDragging) return; setPos({ x: Math.max(0, dragRef.current.initialX + (e.clientX - dragRef.current.startX)), y: Math.max(0, dragRef.current.initialY + (e.clientY - dragRef.current.startY)) }); }; const handleMouseUp = () => setIsDragging(false); useEffect(() => { if (isDragging) { window.addEventListener('mousemove', handleMouseMove); window.addEventListener('mouseup', handleMouseUp); } return () => { window.removeEventListener('mousemove', handleMouseMove); window.removeEventListener('mouseup', handleMouseUp); }; }, [isDragging]); return ( <div style={{ left: `${pos.x}px`, top: `${pos.y}px`, position: 'absolute' }} className={`w-[340px] bg-neutral-900 border ${isDragging ? 'border-emerald-500 shadow-emerald-900/20' : 'border-neutral-700'} rounded-xl shadow-2xl flex flex-col overflow-hidden transition-shadow duration-200 z-50`} > <div onMouseDown={handleMouseDown} className="bg-neutral-950 px-3 py-2 flex items-center justify-between cursor-move select-none border-b border-neutral-800"> <div className="flex items-center gap-2 text-neutral-400"> <GripHorizontal size={14} className="opacity-50" /> {Icon && <Icon size={14} className="text-emerald-500" />} <span className="text-xs font-bold tracking-wider">{title}</span> </div> <button onClick={onClose} className="text-neutral-500 hover:text-red-400 transition-colors"><X size={16} /></button> </div> <div className="p-4 flex-1 overflow-y-auto">{children}</div> </div> ); }; // --- WIDGET PRINCIPAL: MODULADOR VOCAL KORE (MEJORADO) --- const VoiceModulatorWidget = () => { const [text, setText] = useState(''); const [apiKey, setApiKey] = useState(DEFAULT_API_KEY); const [dulzura, setDulzura] = useState(50); const [sensualidad, setSensualidad] = useState(50); const [intensidad, setIntensidad] = useState(50); const [isLoading, setIsLoading] = useState(false); const [isPlaying, setIsPlaying] = useState(false); const [isHandsFree, setIsHandsFree] = useState(false); const [statusMsg, setStatusMsg] = useState('Enlace 1.5 Flash + GCP TTS Establecido.'); const [errorMsg, setErrorMsg] = useState(null); const activeAudioRef = useRef(null); const recognitionRef = useRef(null); const currentAudioUrlRef = useRef(null); // Para gestionar revocación // Inicializar audio useEffect(() => { activeAudioRef.current = new Audio(); activeAudioRef.current.preload = "auto"; return () => { if (activeAudioRef.current) { activeAudioRef.current.pause(); if (currentAudioUrlRef.current) { URL.revokeObjectURL(currentAudioUrlRef.current); } } if (recognitionRef.current) recognitionRef.current.stop(); }; }, []); // Configurar reconocimiento de voz useEffect(() => { if (!('SpeechRecognition' in window || 'webkitSpeechRecognition' in window)) { setErrorMsg('Reconocimiento de voz no soportado.'); return; } const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; recognitionRef.current = new SpeechRecognition(); recognitionRef.current.lang = 'es-ES'; recognitionRef.current.interimResults = false; recognitionRef.current.continuous = false; recognitionRef.current.onresult = async (event) => { const transcript = event.results[0][0].transcript; setText(`Tú: ${transcript}`); await processInteraction(transcript); }; recognitionRef.current.onerror = (event) => { if (event.error !== 'no-speech') { setErrorMsg(`Micrófono: ${event.error}`); setIsHandsFree(false); } }; recognitionRef.current.onend = () => { if (isHandsFree && !isPlaying && !isLoading) { try { recognitionRef.current.start(); setStatusMsg('Escuchando...'); } catch (e) {} } }; // Limpiar al desmontar return () => { if (recognitionRef.current) recognitionRef.current.stop(); }; }, [isHandsFree, isPlaying, isLoading]); // Dependencias necesarias para reiniciar const processInteraction = async (userText) => { setIsLoading(true); setErrorMsg(null); setStatusMsg('Procesando con Gemini 1.5 Flash...'); const key = apiKey.trim(); try { // 1. Llamada a Gemini con timeout const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 5000); const payloadText = { contents: [{ parts: [{ text: userText }] }], systemInstruction: { parts: [{ text: `Eres Kore, 28 años, Huesca. Labia, directa, sensual, disruptiva. Responde en español, máx 20 palabras. REGLA INNEGOCIABLE: CERO PREGUNTAS.` }] } }; const resText = await fetch( `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${key}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payloadText), signal: controller.signal } ); clearTimeout(timeoutId); if (!resText.ok) throw new Error(`Gemini error: ${resText.status}`); const dataText = await resText.json(); const aiText = dataText.candidates?.[0]?.content?.parts?.[0]?.text || "Mmm... vale."; setText(`Kore: ${aiText}`); // 2. Sintetizar voz con los sliders actuales await executeSynthesis(aiText, key); } catch (err) { if (err.name === 'AbortError') { setErrorMsg('Gemini timeout (5s)'); } else { setErrorMsg(err.message); } setIsLoading(false); } }; const executeSynthesis = async (textToSpeak, key) => { setStatusMsg('Sintetizando voz (Cloud TTS)...'); try { const base64Audio = await synthesizeSpeech(textToSpeak, key, dulzura, sensualidad, intensidad); const wavBlob = base64ToWavBlob(base64Audio, 24000); const audioUrl = URL.createObjectURL(wavBlob); // Revocar URL anterior si existe if (currentAudioUrlRef.current) { URL.revokeObjectURL(currentAudioUrlRef.current); } currentAudioUrlRef.current = audioUrl; activeAudioRef.current.src = audioUrl; activeAudioRef.current.onended = () => { setIsPlaying(false); setStatusMsg('Transmisión completada.'); if (isHandsFree) { try { recognitionRef.current.start(); setStatusMsg('Escuchando...'); } catch (e) {} } }; setStatusMsg('Transmitiendo...'); setIsPlaying(true); setIsLoading(false); await activeAudioRef.current.play().catch(err => { throw new Error(`Autoplay bloqueado: ${err.message}`); }); } catch (error) { throw new Error(`Fallo TTS: ${error.message}`); } }; const handleManualPlay = async () => { if (!text.trim()) return setErrorMsg('Escribe algo primero.'); // Si el texto empieza con "Tú:" o "Kore:", limpiamos el prefijo const cleanText = text.replace(/^(Tú:|Kore:)\s*/, ''); if (!cleanText.trim()) return setErrorMsg('Texto vacío después de limpiar.'); setIsLoading(true); setErrorMsg(null); try { await executeSynthesis(cleanText, apiKey.trim()); } catch (err) { setErrorMsg(err.message); setIsLoading(false); } }; const toggleHandsFree = () => { if (!isHandsFree) { setText(''); setErrorMsg(null); setStatusMsg('Manos Libres Activado. Habla...'); // Desbloquear audio en algunos navegadores if (activeAudioRef.current) { activeAudioRef.current.src = SILENT_WAV; activeAudioRef.current.play().catch(() => {}); } try { recognitionRef.current.start(); } catch (e) {} } else { if (activeAudioRef.current) { activeAudioRef.current.pause(); activeAudioRef.current.currentTime = 0; } setIsPlaying(false); setStatusMsg('Sistemas en pausa.'); if (recognitionRef.current) recognitionRef.current.stop(); } setIsHandsFree(!isHandsFree); }; const stopAudio = () => { if (activeAudioRef.current) { activeAudioRef.current.pause(); activeAudioRef.current.currentTime = 0; } setIsPlaying(false); setStatusMsg('Señal interrumpida.'); }; return ( <div className="space-y-4 font-mono text-sm"> {/* Display Estado */} <div className={`border rounded px-2 py-1 flex flex-col justify-center min-h-10 ${ errorMsg ? 'bg-red-950/50 border-red-900' : isHandsFree ? 'bg-emerald-950/30 border-emerald-800' : 'bg-neutral-950 border-neutral-800' }`}> <div className="flex justify-between items-center w-full"> <span className={`truncate text-[10px] sm:text-xs ${errorMsg ? 'text-red-500' : 'text-emerald-500'}`}> > {errorMsg || statusMsg} </span> {isPlaying && !errorMsg && <Activity size={14} className="text-emerald-500 animate-pulse ml-2 flex-shrink-0" />} {isLoading && !errorMsg && <Zap size={14} className="text-amber-500 animate-pulse ml-2 flex-shrink-0" />} {isHandsFree && !isPlaying && !isLoading && !errorMsg && <Mic size={14} className="text-red-500 animate-pulse ml-2 flex-shrink-0" />} </div> </div> {/* Input Texto / Log */} <textarea value={text} onChange={(e) => setText(e.target.value)} className="w-full bg-neutral-950/50 border border-neutral-700 rounded p-2 text-xs text-neutral-300 focus:outline-none focus:border-emerald-500 resize-none h-20" placeholder={isHandsFree ? "Escuchando transcripción en tiempo real..." : "Escribe texto directo o activa Manos Libres..."} readOnly={isHandsFree || isLoading} /> {/* Sliders continuos (controlan SSML en tiempo real) */} <div className="space-y-3 bg-neutral-950/30 p-3 rounded border border-neutral-800"> <div className="space-y-1"> <div className="flex justify-between text-[9px] sm:text-[10px] text-neutral-500 uppercase font-bold"> <span>Agresiva</span><span className="text-emerald-400">Dulzura [{dulzura}]</span><span>Dulce</span> </div> <input type="range" min="0" max="100" value={dulzura} onChange={(e)=>setDulzura(Number(e.target.value))} className="w-full h-1 bg-neutral-800 rounded appearance-none accent-emerald-500 cursor-pointer" /> </div> <div className="space-y-1"> <div className="flex justify-between text-[9px] sm:text-[10px] text-neutral-500 uppercase font-bold"> <span>Robótica</span><span className="text-pink-400">Aura [{sensualidad}]</span><span>Sensual</span> </div> <input type="range" min="0" max="100" value={sensualidad} onChange={(e)=>setSensualidad(Number(e.target.value))} className="w-full h-1 bg-neutral-800 rounded appearance-none accent-pink-500 cursor-pointer" /> </div> <div className="space-y-1"> <div className="flex justify-between text-[9px] sm:text-[10px] text-neutral-500 uppercase font-bold"> <span>Atenuada</span><span className="text-amber-400">Intensidad [{intensidad}]</span><span>Fuerte</span> </div> <input type="range" min="0" max="100" value={intensidad} onChange={(e)=>setIntensidad(Number(e.target.value))} className="w-full h-1 bg-neutral-800 rounded appearance-none accent-amber-500 cursor-pointer" /> </div> </div> {/* Botones de Control */} <div className="flex flex-col sm:flex-row gap-2"> <button onClick={toggleHandsFree} disabled={isLoading} className={`flex-1 py-2 rounded text-xs font-bold flex items-center justify-center gap-2 transition-colors border ${ isHandsFree ? 'bg-red-900/20 text-red-400 border-red-900/50 hover:bg-red-900/40 shadow-[0_0_10px_rgba(239,68,68,0.2)]' : 'bg-indigo-900/20 text-indigo-400 border-indigo-900/50 hover:bg-indigo-900/40' }`} > {isHandsFree ? <MicOff size={14} /> : <Mic size={14} />} {isHandsFree ? 'Detener Escucha' : 'Manos Libres'} </button> <div className="flex gap-2 flex-1"> <button onClick={handleManualPlay} disabled={isLoading || isPlaying || isHandsFree} className="flex-1 bg-emerald-600/20 hover:bg-emerald-600/40 text-emerald-400 border border-emerald-600/50 disabled:opacity-30 py-2 rounded text-xs font-bold flex items-center justify-center gap-1 transition-colors" > {isLoading ? <Loader2 size={14} className="animate-spin" /> : <Play size={14} />} Sintetizar </button> <button onClick={stopAudio} disabled={!isPlaying && !isHandsFree} className="px-4 bg-neutral-800 hover:bg-neutral-700 text-neutral-400 border border-neutral-700 disabled:opacity-30 py-2 rounded text-xs font-bold flex items-center justify-center transition-colors" > <Square size={14} /> </button> </div> </div> {/* Botón para limpiar caché (opcional) */} <div className="text-right"> <button onClick={() => audioCache.clear()} className="text-[8px] text-neutral-600 hover:text-neutral-400 underline" > limpiar caché de audio </button> </div> </div> ); }; // --- ENTORNO ESCRITORIO (sin cambios) --- export default function App() { const [widgets, setWidgets] = useState({ voice: { isOpen: true, pos: { x: window.innerWidth > 768 ? window.innerWidth / 2 - 170 : 20, y: 40 } } }); const toggleWidget = (id) => { setWidgets(prev => ({ ...prev, [id]: { ...prev[id], isOpen: !prev[id].isOpen } })); }; return ( <div className="w-full h-screen bg-neutral-950 bg-[radial-gradient(ellipse_80%_80%_at_50%_-20%,rgba(16,185,129,0.1),rgba(0,0,0,1))] overflow-hidden relative font-sans text-neutral-200"> <div className="absolute inset-0 flex items-center justify-center opacity-[0.02] pointer-events-none"><Settings2 size={500} /></div> {widgets.voice.isOpen && ( <DraggableWidget title="MODULADOR VOCAL KORE" icon={Zap} initialPos={widgets.voice.pos} onClose={() => toggleWidget('voice')}> <VoiceModulatorWidget /> </DraggableWidget> )} <div className="absolute bottom-6 left-1/2 transform -translate-x-1/2 bg-neutral-900/80 backdrop-blur-md border border-neutral-700/50 p-2 rounded-2xl shadow-2xl flex gap-2 z-[100]"> <div className="px-3 flex items-center border-r border-neutral-700/50 text-neutral-500"><LayoutGrid size={20} /></div> <button onClick={() => toggleWidget('voice')} className={`px-4 py-2 rounded-xl flex items-center gap-2 text-sm font-medium transition-all ${
A hyper-realistic, cinematic image of a lone druid standing atop a rugged mountain peak, dressed in ancient, flowing robes with intricate patterns. The druid is surrounded by swirling mist and fog, illuminated by an eerie, glowing (orange orb hovering above his head:1.7) that casts warm, ethereal light over his face and attire. His outstretched arms are raised as he conjures spirits from the ground below, their ghostly forms emerging from the rock as if rising from the depths of the earth. The spirits are ethereal and translucent, their forms stretching and swirling as they ascend around the druid, leaving (trails of glowing orange light:1.6) in the mist. These spectral figures have soft, flowing shapes that merge with the fog, creating a hauntingly beautiful display of light and shadow. The trails of orange glow fade into the mist, adding an otherworldly effect that enhances the supernatural energy of the scene. The mountain peak is rugged, with jagged rocks and a vast landscape stretching out into the distance. Dark clouds loom overhead, partially obscuring the sky, while volumetric godrays break through in scattered beams, adding dramatic lighting that illuminates the scene and emphasizes the swirling fog. The air is thick with floating particles and a soft, chilling mist that clings to the ground, giving the scene an ancient, mystical feel. The entire image captures the power and mystery of the druid’s ritual, with the vivid contrast of orange light against the dark, foggy landscape creating a cinematic and awe-inspiring atmosphere." Key Elements with Weights: (Hyper-realistic, cinematic atmosphere:1.5) (Ancient druidic clothing, intricate robes and symbols:1.5) (Glowing orange orb hovering above his head:1.7) (Ethereal spirits rising from the ground, translucent forms:1.6) (Orange trails left by the spirits in the mist and fog:1.6) (Volumetric godrays, dynamic lighting through fog:1.4) (Dark, rugged mountain peak with jagged rocks:1.3) (Chilling, supernatural atmosphere:1.5) Abstract Painting Prompt Prompt: "An abstract, mystical painting of a druid standing on top of a jagged mountain, conjuring ethereal spirits from the ground below. The druid is adorned in ancient robes with intricate designs, his figure partially obscured by swirling mist and fog. Above his head floats a (glowing orange orb:1.7), casting an otherworldly light over his face and clothing, and adding a surreal, magical quality to the scene. The spirits he summons rise from the rocky ground, their forms ghostly and translucent, flowing upwards in long, graceful trails that (leave vibrant orange marks in the mist:1.6). The spirits blend into the fog with a dreamlike, almost liquid motion, their shapes abstracted and fluid. The orange trails contrast with the dark, moody fog, adding a mystical energy to the painting. The mountain peak is depicted with dark, textured brushstrokes, blending into a foggy expanse that stretches into the distance. Soft, diffused godrays pierce through the mist, casting light across the druid and the spirits, creating depth and a sense of motion in the swirling fog. The overall composition is both haunting and enchanting, capturing a moment of ancient magic with surreal colors and abstract forms that emphasize the beauty and power of the druid’s ritual."
a photorealistic limited edition sci-fi futuristic hovering jet powered shoe, NIKE, sparks, hovering, sci-fi, futuristic, jet powered, photograph, highly detailed, intricate, hyper realistic, ornate, luxury, cinematic, cgsociety, 8k Ultra Quality, sharp focus
Ultra-photorealistic cinematic film still of Alicia Vikander reimagined as the Grim Reaper, blending her natural beauty with an aura of haunting power and timeless authority. Her face is ethereal and striking, her sharp cheekbones and soft jawline accentuated by the cold, spectral glow of the surrounding light. Her skin is an ashen, porcelain hue, smooth yet unnervingly flawless, with faint, silvery veins visible just beneath the surface. Her deep brown eyes glow faintly with an otherworldly silver light, exuding both wisdom and an unrelenting inevitability. Her lips, slightly parted, are a dark, muted plum, adding to her haunting yet elegant appearance. Her hair is raven black, cascading in long, flowing waves that shimmer faintly as if touched by a supernatural breeze. Wisps of silvery mist weave through the strands, catching the dim light and creating an almost halo-like effect. Around her head hovers a faint, incorporeal crown of glowing runes, shifting and flickering like dying embers, signifying her dominion over life and death. She is dressed in an avant-garde interpretation of the Reaper’s cloak: a sleek, black, floor-length robe with intricate textures resembling flowing smoke and shadows. The fabric seems almost alive, shifting subtly as though it’s a part of the darkness itself. The edges of the cloak are frayed and dissolve into ethereal mist, giving her an otherworldly, intangible quality. Beneath the robe, glimpses of silvery armor etched with ancient, cryptic symbols are visible, hinting at her role as a celestial enforcer. Her hood is drawn back, revealing her face, but the shadows of the hood frame her features in a dramatic, gothic contrast. In her right hand, she wields a scythe unlike any other: its massive blade is forged from a gleaming black metal that reflects faint, ghostly images of souls. The staff is carved from a dark, polished wood entwined with glowing silver runes that pulse faintly, as though alive. Her left hand hovers slightly, trailing a faint mist of spectral energy that curls and dissipates into the surrounding air. The background is a surreal, otherworldly landscape: a vast, barren expanse shrouded in mist, with jagged, obsidian-like rock formations rising into the sky. The horizon glows faintly with an eerie, greenish-blue light, as if it’s the border between the world of the living and the dead. Shadowy silhouettes of wandering souls drift aimlessly in the distance, their faint whispers almost audible in the stillness. Above, the sky is a chaotic swirl of dark clouds, pierced by occasional streaks of ethereal light that illuminate the scene in fleeting bursts. The lighting is dramatic, with cold, pale blue and green tones dominating the scene, casting Alicia’s figure in sharp relief. The glowing runes on her armor and scythe cast subtle, shifting light patterns on her robes and the ground. Her face is illuminated by a soft, ghostly glow, emphasizing her beauty while adding an unnerving edge to her expression. Shadows play dynamically across her figure, enhancing the ethereal, otherworldly atmosphere. Her expression is calm and resolute, with a faint, enigmatic smile that suggests she understands the inevitability of her role. Her eyes convey both compassion and an uncompromising sense of duty, embodying the dual nature of the Grim Reaper as both a harbinger of death and a guide for lost souls. There’s a sense of timeless authority in her posture, as though she has walked the boundary between life and death for eternity. This ultra-photorealistic image is indistinguishable from a professional cinematic film still, with every detail—from the textures of her cloak and scythe to the eerie, atmospheric backdrop—rendered in breathtaking precision. The mood is chilling, majestic, and steeped in gothic gravitas.
Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3 Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3
An abstract, surreal painting of a hovering druid in an ancient forest clearing, surrounded by a glowing, atomic halo of vibrant orange light that forms swirling, concentric patterns around him. The druid’s white, flowing robes seem to blend into the air, merging with streaks of glowing energy and organic shapes, giving his form a soft, ethereal quality. His arms are extended outward, and instead of a defined face, his features are softened, almost dreamlike, as if part of the forest itself. The clearing is encircled by towering, ancient stones with glowing, fragmented runes etched into them. The runes emit a pulsing light that melts into warm, luminous trails that float upward, diffusing like ink in water. Around him, ethereal spirits appear as loose, wispy forms, their shapes abstracted and blending seamlessly with the flowing fog and mist. Their spectral trails leave fiery orange and red streaks that illuminate the air, creating a rhythm of light and color that guides the viewer’s eye through the piece. The forest background is painted in soft, blurred strokes, with deep shades of green, teal, and hints of shadowy blue. Light particles and embers float throughout the scene, giving it a textured, atmospheric quality. Godrays of diffused, golden light filter through the treetops, fragmenting as they pass through the mist and mingling with the energy surrounding the druid. The composition emphasizes motion and energy, with swirling patterns and soft lines blending into each other, capturing a sense of mystery and ancient magic in a surreal and dreamlike manner." Key Elements with Weights (Abstract, surreal painting style:1.6) (Druid in white robes, hovering, ethereal form:1.5) (Atomic halo of vibrant orange light:1.7) (Glowing, fragmented runes on ancient stones:1.4) (Spectral spirits with abstract, fiery orange trails:1.5) (Soft, blurred forest with deep green and blue hues:1.3) (Volumetric godrays fragmented by the mist:1.4) (Flowing embers and light particles for texture:1.3)
a photorealistic limited edition sci-fi futuristic hovering jet powered shoe, NIKE, sparks, hovering, sci-fi, futuristic, jet powered, photograph, highly detailed, intricate, hyper realistic, ornate, luxury, cinematic, cgsociety, 8k Ultra Quality, sharp focus
In a desolate, rocky volcanic landscape filled with jagged, black volcanic rocks and deep craters scattered across the ground, a lone man stands with his arms outstretched to his sides, gazing directly into the camera with an intense expression. The ground around him is covered by a thin layer of smoke or mist that hovers about 15 cm above the surface, partially obscuring the rugged terrain. Subtle veins of glowing lava pulse beneath the smoke, casting a faint, eerie orange-red light that makes the mist appear to glow and ripple softly across the rocks. Above his head, a floating atomic symbol—exactly like the classic atomic design with three orbits circling a central nucleus—glows in a vibrant atomic green, emanating a powerful, supernatural light. This emblem seems to represent an almost mystical source of energy, hovering just above him and illuminating his face with a greenish hue. From his eyes and mouth, a slow, controlled stream of molten lava flows, pouring out like fiery tears and a searing, molten torrent. The lava glows in a deep, vivid orange and flows down in thick rivulets, casting flickering shadows and illuminating the contours of his face. The intense heat radiates from his body, causing wisps of smoke and heat waves to rise from his skin, blending into the smoky ground fog. Background and Surroundings: The volcanic landscape stretches endlessly, with dark, sharp rocks and lava veins glowing subtly through the mist, adding a mysterious depth to the scene. The subtle interplay of the green atomic glow above him and the warm lava light below creates a haunting, otherworldly atmosphere. Embers and faint particles of ash drift lazily in the air, catching the light from the lava and adding an ethereal quality. Rendering Style: Hyperrealistic with intricate, volumetric lighting, emphasizing the textures of the rocks, mist, and the glowing atomic symbol. Every detail, from the flowing lava and subtle ground lighting to the distinct atomic symbol glowing in green above his head, is meticulously rendered, creating a powerful and surreal visual. Ideal for ArtStation, this masterpiece balances the mysticism of the atomic symbol with the raw power of volcanic energy, captured in an epic cinematic composition.
A high-angle, full-body anime illustration of a beautiful sorceress hovering in mid-air. The perspective is looking down at her from above. She has long black hair, purple eyes, and a silver cross earring. She is wearing a high-cut black bodysuit with a sheer fishnet halter-neck and ornate silver metallic armor plating on the bust. Her skin is rendered with a hyper-realistic wet, glossy texture with strong highlights. She poses dynamically with knees bent and arms outstretched downwards, fingers splayed. Behind her, large four-pointed silver metallic shuriken blades float in a radial pattern. The background is a dark starry void with grey rocks visible far below. Masterpiece, 8k resolution, dynamic composition, shiny metal textures. High-angle shot, bird's-eye view, hovering pose, wet skin texture, glossy latex, floating weapons, shuriken, starry night, anime semi-realism, cinematic lighting.
Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3 Ghostly spectre hovering above an ancient cemetery under a blood-red full moon::1.5 digital painting in cinematic gothic style::1.3 half-dead, half-alive armored silhouette with translucent tattered cape::1.5 hollow eyes glowing faintly, ethereal glow flickering across gaunt features::1.4 undulating mist rising like souls on a summer heat haze::1.3 moss-covered tombstones and twisted oaks shrouded in fog::1.2 volumetric moonlight shafts and dramatic chiaroscuro sculpting his form::1.5 ultra-detailed textures, moody supernatural atmosphere::1.3