Canalización estándar para videos de letras
Un conjunto de estilos fijos ya validados. El usuario solo necesita proporcionar el archivo de audio y el nombre del artista; todo lo demás se ejecuta según este estándar, sin rediseñar la apariencia. Las letras oficiales son opcionales: si se proporcionan, se sigue la ruta rápida 3A (sin conjeturas de texto); si no, se sigue la ruta de reconstrucción 3B. En ambas rutas, la línea de tiempo debe basarse en mediciones reales.
Entrada / Salida
| Entrada | Descripción |
|---|---|
| Audio | Obligatorio. Cualquier archivo MP3/WAV/FLAC, de duración ilimitada |
| Nombre del artista | Obligatorio. Se utiliza para la introducción, la marca de agua constante y los créditos finales |
| Letras oficiales (opcional) | Si el usuario las tiene, se priorizan: sin conjeturas de texto, pero la línea de tiempo se obtiene completamente mediante reanálisis con indicaciones (ver 3A). Si no se proporcionan, se reconstruyen con Whisper (3B) |
| Título de la canción (opcional) | Por defecto se toma del nombre del archivo; Artista - Título.mp3 elimina automáticamente el prefijo del artista |
| Frase temática (opcional) | Una línea en chino debajo del título de la introducción; por defecto no se muestra |
Salida: <proyecto>/renders/video.mp4 + renders/contact-sheet.jpg + transcript.json + NOTES.md.
Especificaciones fijas (no modificables)
- Pantalla vertical 1080 × 1920, 30 fps, duración total igual a la duración real del audio (sin recortar, estirar ni cambiar la velocidad).
- El audio se usa en su archivo original; el marco de trabajo lo normaliza durante el renderizado (
data-volume="0.9"). - Fondo: de negro puro a azul violáceo muy oscuro, con dos grupos de motas de luz desfasadas que se desvanecen lentamente; no usar filtros CSS de pantalla completa (dejan uniones visibles).
- Elemento principal: un solo vidrio transparente, con 8 contornos geométricos redondeados (esfera/elipse horizontal/elipse vertical/rectángulo redondeado/rombo/triángulo/pentágono/hexágono). Al chocar con los bordes de la pantalla, se comprime un 11% a lo largo de la normal → se transforma en otro contorno en 0.94 s → rebota y continúa desplazándose. Línea de tiempo única para toda la canción, no se reinicia al cambiar de letra.
- Parte inferior: banda de animales (gato/oso/pingüino/zorro) en bucle continuo de 4.5417 s, pegada al fondo, 1080×526, capa por encima de las motas de luz y por debajo del vidrio,
mix-blend-mode: screen. - Subtítulos: todo el bloque centrado verticalmente en el centro óptico de la imagen (y≈960); inglés/texto original arriba (Inter, tamaño de fuente autoajustable según el ancho de la línea), chino abajo (SimSun, color marrón cálido
#DDA97D), con fundido de entrada/salida en grupo. - Los créditos utilizan el nombre del artista, no aparecen etiquetas de párrafo como VERSE / CHORUS / BRIDGE en la imagen.
Los parámetros de detalle, colores, tamaños de fuente y aspectos técnicos se encuentran en references/style-spec.md.
Flujo de trabajo
Supongamos que HF = directorio raíz de instalación de HyperFrames (por defecto G:\hyperframes),
PY = Python con faster-whisper (por defecto C:\Users\Admin\.workbuddy-ai\binaries\python\envs\hf\Scripts\python.exe),
SK = directorio de este skill.
1. Crear proyecto
"$PY" "$SK/scripts/new_project.py" "<ruta_del_audio>" "<nombre_del_artista>" [--title "título"] [--tagline "una línea en chino"] [--root G:/hyperframes/videos]
Genera la ruta absoluta del proyecto; anótela obligatoriamente. Crea un directorio único → copia el audio → lo convierte a WAV mono de 16 kHz → ejecuta el script oficial analyze-beatgrid.py generando audiomap.json → copia el sprite de la banda de animales y GSAP → escribe project.json.
Nunca sobrescribir un directorio de proyecto existente; para modificar un trabajo ya entregado, debe crearse un nuevo proyecto.
2. Transcripción
"$PY" "$SK/scripts/transcribe.py" "<proyecto>" --model medium.en --lang en
Para una canción de 3–4 minutos en CPU int8 se tarda unos 3–6 minutos. Para canciones que no sean en inglés, cambie --lang al idioma correspondiente y quite el .en de --model.
Genera whisper_raw.json (con marcas de tiempo a nivel de segmento y palabra).
Canciones multilingües (inglés+japonés, inglés+coreano, etc.) no usar transcribe.py: --lang fija un solo idioma y el otro será transcrito forzosamente como el primero — el resultado sonará coherente pero estará completamente equivocado, peor que no tener solución.
En su lugar, use la detección automática por ventanas:
"$PY" "$SK/scripts/transcribe_mixed.py" "<proyecto>" --model "C:/Users/Admin/.cache/whisper-manual/large-v3-turbo"
Decodifica en ventanas de 14 s / paso de 10 s, cada ventana determina su propio idioma, conserva solo las palabras centrales de la ventana (las zonas superpuestas se descartan, sin duplicados), imprime un mapa de idiomas y escribe whisper_mixed.json. Revise primero el mapa de idiomas: los puntos donde cambia el idioma suelen ser los límites de las estrofas.
Tenga en cuenta que los modelos .en (medium.en / small.en) son exclusivamente para inglés y físicamente no pueden generar japonés; las canciones multilingües deben usar modelos multilingües.
Para la ruta 3A (con letras oficiales), este paso es opcional: la línea de tiempo se basa en el reanálisis con indicaciones, pero para canciones multilingües sigue siendo recomendable ejecutar primero transcribe_mixed.py para ver el mapa de idiomas y determinar si cada ventana debe usar --lang en o --lang ja.
3. Escribir el archivo fuente de letras (este paso debe hacerse manualmente)
Primero, decida qué ruta seguir y luego escriba <proyecto>/lyrics_source.json.
3A El usuario ha proporcionado letras oficiales (ruta preferida)
- El texto se toma tal cual de las letras oficiales, sin cambios: el campo
enes el texto original oficial (las frases en inglés se ponen en inglés, las frases en japonés se ponen en japonés, los kanji/kana se escriben exactamente),zhes la traducción frase por frase. - La línea de tiempo no se obtiene de una transcripción completa de la canción: se usan las letras oficiales como
initial_promptpara reanálisis por zonas y obtener marcas de tiempo a nivel de palabra (ver 3.5), y cada línea tienestart/endexplícitos.align_lyrics.pyusará el canal manual para anclar cada línea. - Si las letras oficiales no coinciden con lo que se escucha en el reanálisis → se adopta lo oficial en todos los casos, y las diferencias se listan en NOTES.md para que el usuario las confirme. Tres casos comunes: forma estándar vs error tipográfico (不甲斐ない / 不甲埋ない), palabras con pronunciación similar (束の間の / 明日の間の), confusión de consonantes sordas (smiling / falling).
- Si en las letras oficiales hay dos líneas separadas que se cantan de una sola vez y cada una dura menos de 1 segundo → los subtítulos se fusionan en una sola línea (el texto no cambia, solo se muestran juntas), y la tabla de fusiones se escribe en NOTES.md.
- Las onomatopeyas o improvisaciones no presentes en las letras oficiales ("Yeah,", tarareos, silbidos) → no se subtitulan, no se inventan.
- La estructura se escribe así:
{
"audio": "Artista - Título.mp3",
"models": ["large-v3-turbo — reanálisis por zonas con indicación, letras oficiales como initial_prompt"],
"lines": [
{"en": "Oh my god, they're coming again", "zh": "Oh Dios mío, están llegando otra vez", "start": 6.36, "end": 9.10, "lang": "en", "conf": "high"}
]
}
3B Sin letras oficiales (ruta de reconstrucción)
Lea whisper_raw.json (o whisper_mixed.json para canciones multilingües) y escriba el archivo fuente:
{
"audio": "Artista - Título.mp3",
"models": ["faster-whisper medium.en — pista completa, marcas de tiempo por palabra"],
"lines": [
{"en": "Sometimes I lay under the moon", "zh": "A veces me acuesto bajo la luna"},
{"en": "And thank God I'm breathing", "zh": "Y agradezco a Dios por seguir respirando"}
]
}
Requisitos comunes para ambas rutas:
- Cada línea = una frase real cantada, con la puntuación de las letras oficiales; si Whisper fusiona frases demasiado largas, deben dividirse.
- La ortografía en inglés debe corregirse según las letras oficiales (conservar apóstrofos como
'Cause/I've), no copie los errores de Whisper. - La traducción al chino debe ser frase por frase, con el tono adecuado a la canción, sin omitir frases, sin fusionar, sin texto de relleno.
- Los estribillos repetidos deben escribirse cada vez (no usar "ídem").
- Si alguna frase está cubierta por el acompañamiento y la alineación automática es claramente incorrecta, asígnele un tiempo explícito (escuche el audio primero antes de rellenar):
{"en": "...", "zh": "...", "start": 13.22, "end": 16.80}. - Canciones en chino / música instrumental: en las canciones en chino, ponga el texto original en
eny dejezhcomo cadena vacía; en la pantalla solo se muestra una línea. La música instrumental no es aplicable a este skill. - Canciones multilingües: el campo
endebe contener el texto original en el idioma real de la frase (inglés en inglés, japonés en japonés, kanji/kana tal cual),zhdebe contener la traducción al chino. La pantalla está diseñada para mostrar "texto original + chino" en dos líneas, por lo que la combinación no rompe el diseño; la línea en inglés de la plantilla ya incluyeYu Gothic UI / Yu Gothic / Meiryocomo fuentes de respaldo, por lo que los kana y kanji se mostrarán correctamente.
3.5 Reanálisis con indicaciones: método clave para la precisión de la línea de tiempo
La transcripción completa de la canción suele ser imprecisa para canciones rápidas, coros simultáneos o frases habladas rápidamente. Usar las letras conocidas como initial_prompt para reanálisis por zonas es el método de mayor precisión — la calidad de la indicación determina directamente la precisión del reanálisis: letras oficiales > letras adivinadas > sin indicación. Se puede escribir un script rescan_all.sh para procesar toda la canción en lote:
"$PY" "$SK/scripts/rescan.py" "<proyecto>" --from 6.0 --to 20.0 \
--model "C:/Users/Admin/.cache/whisper-manual/large-v3-turbo" \
--lang en --prompt "<texto original oficial de esa zona>"
Puntos clave:
- Dividir toda la canción en zonas de 20–26 s, con un solapamiento de aproximadamente 0.5 s entre zonas, y reanalizar cada zona por separado;
large-v3-turboen CPU int8 tarda unos 5–10 s por zona. - Para canciones multilingües, cada zona se reanaliza dos veces (una con
--lang eny otra con--lang ja): solo se adoptan las marcas de tiempo a nivel de palabra que coincidan en ambas pasadas; las palabras que aparecen solo en una pasada se verifican con la otra. - Para frases con arranque dudoso o poco claro, añada una zona estrecha de 2 s para fijar el inicio (
--from 37.2 --to 39.2solo para fijar el inicio de una frase); el inicio de las zonas divididas suele verse afectado por el final de la frase anterior. - Manejo de alucinaciones: el reanálisis puede, de forma esporádica, repetir la misma frase completa 15+ veces → esa zona se descarta, se cambia al canal del otro idioma o se reduce la zona y se reanaliza, y solo se toman las marcas de tiempo limpias y únicas; no escriba las marcas de tiempo de las alucinaciones en el archivo fuente de letras.
- Cada reanálisis guarda
rescan_<from>-<to>.jsone imprime el inicio y fin de cada palabra dentro de la zona. Esto es una segunda decodificación, no una estimación; los números se escriben directamente enstart/enddelyrics_source.json. - Para pasajes cubiertos por el acompañamiento, procesados como
ooh, fusionados en un bloque grande, o frases repetidas que se han tragado, use este mismo método primero para escribir valores de cobertura; cambiar el modelo (small.en/medium.en) también es efectivo.
4. Alineación + Generación
"$PY" "$SK/scripts/align_lyrics.py" "<proyecto>"
"$PY" "$SK/scripts/motion.py" "<proyecto>"
"$PY" "$SK/scripts/gen_storyboard.py" "<proyecto>"
"$PY" "$SK/scripts/build_frame.py" "<proyecto>"
"$PY" "$SK/scripts/make_index.py" "<proyecto>"
align_lyrics.py imprimirá una tabla línea por línea con los tiempos y realizará dos comprobaciones estrictas:
- Si alguna frase al inicio o final no coincide → sale con error e indica qué frases son (no usará tiempos adivinados para encubrirlo).
- Si alguna frase del medio no coincide, o dos frases tienen el mismo inicio, o alguna frase se comprime por debajo de 0.5 s → imprime
REVIEWy sale con código 2.
Si ocurre alguno de estos casos, vuelva al paso 3.5 y añada valores de cobertura hasta que la salida sea OK: every line anchored to a real sung timestamp.
No omita este paso para renderizar. En la ruta 3A, todas las líneas tienen start/end explícitos (canal manual), por lo que todas deberían estar ancladas, sin huecos; en la ruta 3B, se basa en la coincidencia de palabras; las líneas que no coincidan se les asignan tiempos según el paso 3.5.
La semilla aleatoria de motion.py está determinada por el "nombre del artista + título de la canción" — para una misma canción, cada reejecución genera una trayectoria idéntica; canciones diferentes generan trayectorias diferentes.
5. Verificación
cd "<proyecto>"
node "$HF/packages/cli/bin/hyperframes.mjs" check .
node "$HF/packages/cli/bin/hyperframes.mjs" snapshot . --at "3,20,60,120" --no-end -o build/shots
Debe cumplirse: lint / runtime / layout / motion 0 errores, y el contraste debe pasar WCAG AA.
Luego revise las capturas de pantalla : el vidrio debe estar completo, las letras centradas, la banda de animales en la parte inferior, sin burbujas ni etiquetas de párrafo sobrantes.
composition_file_too_large es una advertencia de estilo cuando un archivo de composición supera las 300 líneas; es aceptable (es inevitable en un diseño de línea de tiempo única).
6. Renderizado + Aceptación
node "$HF/packages/cli/bin/hyperframes.mjs" render . --skill=music-to-video -q delivery -o renders/video.mp4 --fps 30
"$PY" "$SK/scripts/verify.py" "<proyecto>"
verify.py verificará la resolución, la tasa de fotogramas y la desviación de duración (si supera un fotograma, se reporta un error) y generará una hoja de contactos. Es obligatorio revisar la hoja de contactos para confirmar que la introducción, las estrofas, los estribillos, las repeticiones y el final tengan imágenes, y que los subtítulos no tengan espacios vacíos o desalineaciones.
Finalmente, escriba NOTES.md (especificaciones, origen de la línea de tiempo, comandos de reproducción, registro de verificación) y entregue el video final y la hoja de contactos al usuario.
Disciplinas estrictas
- No usar BPM/cuadrícula de compás para deducir los tiempos de las letras; las marcas de tiempo solo pueden provenir de mediciones reales de Whisper (incluido el reanálisis con indicaciones) o de tiempos de cobertura obtenidos al escuchar manualmente.
- Las letras oficiales proporcionadas por el usuario tienen prioridad sobre cualquier resultado de reconstrucción del modelo; si hay conflicto, se adopta lo oficial y las diferencias se escriben en NOTES.md para que el usuario las confirme.
- No cambiar la resolución, la tasa de fotogramas, la duración, los créditos o la posición de los subtítulos solo porque "se vea mejor".
- No afirmar sincronización audiovisual sin haberla verificado; cada canción debe revisarse fotograma a fotograma.
- Si faltan materiales, herramientas no disponibles o hay conflictos de especificaciones → informar claramente, no usar datos estimados para fingir resultados.
- Los proyectos ya entregados y sus archivos fuente se consideran de solo lectura; cualquier modificación debe realizarse en un nuevo directorio.
Dependencias del entorno
- HyperFrames:
node <HF>/packages/cli/bin/hyperframes.mjs(incluyeskills/music-to-video/scripts/analyze-beatgrid.py). - Python:
faster-whisper,librosa,soundfile,numpy,scipy,pillow. Si faltan:python -m venv <env> && <env>/Scripts/pip install faster-whisper librosa soundfile numpy scipy pillow. - Modelos Whisper: pasando el nombre del modelo se descarga automáticamente; también se puede apuntar a un directorio local (en esta máquina se usa comúnmente
C:\Users\Admin\.cache\whisper-manual\medium.en; para reanálisis con indicaciones se prefiereC:\Users\Admin\.cache\whisper-manual\large-v3-turbo; en CPU int8, una canción completa tarda unos 48 s, una zona 5–10 s). - ffmpeg / ffprobe deben estar en el PATH.
Problemas conocidos
- No usar
<video>para la capa de la banda:hyperframes renderen Windows falla al enlazar el directorio de fotogramas extraídos al directorio de compilación (errorENOENT ... __hyperframes_video_frames/*/frame_00001.jpg). Usar sprite. - El filtro CSS de pantalla completa
filter: hue-rotate()deja juntas verticales en el renderizado; usar dos capas de motas de luz con fundido cruzado. - Las plantillas HTML con
data-composition-idno deben colocarse en el directorio raíz del proyecto (activamultiple_root_compositions); colocarlas enbuild/. - Las fuentes chinas deben declararse explícitamente con
@font-face { src: local("SimSun"); }, de lo contrario, lint reportafont_family_without_font_face. - Alucinaciones en el reanálisis: ciertas zonas pueden repetir la misma frase completa 15+ veces (por ejemplo, 「永遠の行方はどこにある?」×15) → esa zona se descarta, se cambia al canal en inglés intercalado o se reduce la zona y se reanaliza; no escribir las marcas de tiempo de las alucinaciones en el archivo fuente de letras.
- Inconsistencias entre letras oficiales y reanálisis: adoptar lo oficial y listar la tabla comparativa en NOTES.md para que el usuario confirme; no modificar el texto oficial por cuenta propia.
- Líneas rápidas fusionadas: en las letras oficiales, si dos líneas separadas se cantan de una sola vez y cada una dura menos de 1 segundo, los subtítulos se fusionan en una sola línea (el texto no cambia, solo se muestran juntas), la tabla de fusiones se escribe en NOTES.md.
- Error de carga de fuentes en modo headless (Yu Gothic UI muestra FAILED al extraer fotogramas) no es motivo de alarma: las fuentes con
src: local()que están instaladas en el sistema suelen ser encontradas por el navegador, la siguiente fuente de respaldo también funcionará; primero revise las capturas de pantalla antes de decidir si intervenir.