Narración
npm run tts vocaliza cada línea de src/timeline.ts en public/audio/<lang>/<key>.wav, con
las marcas de tiempo por palabra de Cartesia en <key>.words.json, y reescribe src/audio.generated.ts con las
duraciones reales de las líneas. Las líneas se almacenan en caché según motor, voz, modelo, velocidad y texto: solo las líneas nuevas o editadas se vuelven a vocalizar. npm run tts -- en o -- es limita la ejecución a un solo idioma.
Configuración de Cartesia
- Los ajustes se encuentran en
video.config.mjs(tts): modelosonic-3.6-2026-08-27, velocidad1. - Voz EN:
5568a7df-e5ab-4442-9fae-2e9ba1b15ad8(en-GB). CARTESIA_API_KEYse busca en este orden:- las variables de entorno;
.enven la carpeta del video (ignorado por git; copia.env.example);~/Documents/repos/.env.
- Nunca imprimas la clave, la hagas eco o la subas al repositorio. Verifica que exista sin mostrarla, por
ejemplo
test -n "$CARTESIA_API_KEY" && echo set.
Motores y el fallback Kokoro
tts.engine en video.config.mjs:
auto(por defecto): Cartesia cuando se encuentra una clave y el servicio responde; en caso contrario, todo el idioma se vocaliza con Kokoro (local, gratuito) y se muestra una advertencia. Un idioma nunca es una mezcla de dos voces: si Cartesia falla a mitad de camino, todo el idioma se vuelve a hacer con Kokoro, y la siguiente ejecución con Cartesia de nuevo lo repite.cartesia: falla en lugar de recurrir al fallback.kokoro: permanece offline. Voces:af_bella(EN),ef_dora(ES). Kokoro no proporciona marcas de tiempo por palabra, por lo que los subtítulos se temporizan de forma proporcional.
Una advertencia sobre Kokoro en la salida significa que el video no tiene la voz de Cartesia: indícalo al entregar el video.
Voz en español
voices.es es null hasta que se cree una versión en español. Elige una voz en español de la biblioteca
de voces de Cartesia, escucha una línea de muestra del guion, registra su ID en video.config.mjs
(es: { id: '<voice-id>', language: 'es' }), y añade 'es' a SETTINGS.voices (y a
SETTINGS.subtitles si se desean subtítulos en español).
Pronunciación
El TTS lee el texto de la narración; los subtítulos muestran el texto escrito. Cuando el TTS lee mal
un número o símbolo, asigna a la línea una forma hablada como cuarto argumento de say, y mantén
la forma escrita del artículo como subtítulo:
say(0.3, 'It solved 14/20 tasks.', 'Resolvió 14/20 tareas.', {
en: 'It solved fourteen out of twenty tasks.',
es: 'Resolvió catorce de veinte tareas.',
})
- El TTS vocaliza la forma hablada (y la caché vuelve a vocalizar la línea cuando esta cambia); la duración de la línea se obtiene de ella.
- El subtítulo muestra el texto escrito, temporizado proporcionalmente durante la línea (las marcas de tiempo por palabra pertenecen a las palabras habladas, por lo que no se usan para esa línea).
- Escribe la línea de forma simple ("14/20", "83 %", "p = 0.0018") y añade una forma hablada solo cuando al escuchar notes que el TTS se equivoca.
- Las barras inclinadas, cadenas de versión ("gpt-5.6"), nombres de archivo y acrónimos son los problemas habituales. Escucha cada línea que contenga alguno de estos.
QA de audio
npm run audio-qa (escribe out/audio-qa.md):
- normaliza cada línea al mismo volumen (una vez por versión de archivo);
- marca una línea como recortada cuando su pico de origen alcanza -0.1 dBFS;
- marca una línea como demasiado larga cuando dura más de 1.6x su estimación basada en la longitud del texto: señal de habla distorsionada o repetida. Escucha cada línea marcada y vuelve a vocalizarla (edita ligeramente el texto, o elimina su archivo wav) si está mal;
- después de
npm run render, mide cada mezcla enout/con respecto a -16 LUFS integrados, ±1 LU (pico verdadero -1.5 dBTP) y sale con código 1 si alguna no cumple el objetivo.
Después de narrar, ejecuta npm run lint nuevamente: las duraciones reales reemplazan las estimaciones y pueden
hacer que el video se desvíe de su duración objetivo o que un subtítulo supere los 17 caracteres/s.
Música de fondo
Se genera una música suave localmente (npm run music, creada automáticamente por npm run render cuando
falta; necesita python3 con numpy, o uv). Su volumen se reduce al 30 % mientras se reproduce una línea. Establece
SETTINGS.music en null para no tener música.