Motor de Sincronización de Palabras de Letras
Resuelve el problema común en videos líricos donde la visualización de líneas completas se siente lenta o se retrasa respecto a la interpretación del cantante, sincronizando cada palabra individual con su inicio acústico.
flowchart TD
A[Archivo de Audio MP3/WAV] --> B[Análisis de Envolvente Acústica RMS]
B --> C[Reconocimiento de Voz / Puntos de Control Vocales]
C --> D[Ponderación de Sílabas por Palabra]
D --> E[Marcas de Tiempo Sub-segundo por Palabra JSON]
E --> F[Revelado Progresivo de Palabras HTML/CSS]
Problema y Solución
- El Problema: Cuando un cantante acelera (por ejemplo, charanams rápidos o líneas en staccato), una revelación estática de línea completa permanece en pantalla demasiado tiempo o aparece después de que el cantante ya ha terminado.
- La Solución:
- Cada línea se tokeniza en palabras individuales:
["Kanmani", "anbodu", "kaadhalan"]. - La duración vocal activa se distribuye según pesos silábicos y aumentos localizados de energía RMS.
- Todas las palabras de la línea se renderizan simultáneamente en el diseño para que el tamaño de fuente y el ajuste de línea nunca salten.
- Las palabras próximas se renderizan en tinta fantasma tenue (
opacity: 0.12). - A medida que el cantante pronuncia cada palabra, esa palabra específica transiciona a un rojo carmesí sólido (
opacity: 1,color: #8c1226).
- Cada línea se tokeniza en palabras individuales:
Script de Extracción de Marcas de Tiempo
import wave, numpy as np
def compute_word_timings(audio, words, t_start, t_end, sr=16000):
dur = t_end - t_start
active_dur = max(0.4, dur * 0.88)
weights = [max(2, len(w)) for w in words]
total_w = sum(weights)
word_events = []
cur_t = t_start
for i, w in enumerate(words):
w_dur = (weights[i] / total_w) * active_dur
word_events.append({
'word': w,
't_rel': round(cur_t, 3)
})
cur_t += w_dur
return word_events
Resaltado Progresivo de Palabras con CSS
.native .word {
display: inline-block;
opacity: 0.12;
transition: opacity .12s ease-out;
padding: 0 .25cqw;
}
.native .word.revealed {
opacity: 1;
}