Beat sync (Sincronización de ritmo)
Scripts en scripts/:
beats.py MUSIC --out beats.json [--start S --end E]— tempo, pulsos, estimación de downbeats, ataques fuertes (golpes), energía RMS por segundo.timemap.py— construye un mapa de tiempo de velocidad variable (rampas de velocidad) y lo renderiza fotograma a fotograma con ffmpeg.
1. Analizar la música
python beats.py song.mp3 --out edit/beats.json
Produce tempo, beats (s), downbeats (cada 4º pulso desde la fase más fuerte), hits (ataques fuertes: caja/bombo/drops), energy cada 0.5 s.
Interpretación del resultado:
- Periodo de pulso
P = 60 / BPM. A 30 fps un pulso a 120 BPM = 15 fotogramas. - Los golpes no siempre son pulsos. Acentos vocales, finales de risers, drops. Para las tendencias "edit audio" los puntos de corte son los golpes, no el metrónomo.
- librosa puede bloquearse a la mitad o el doble del tempo. Verificación lógica: pop típico 90-130 BPM, trap 130-160 (a menudo se siente como half-time), EDM 120-128, phonk 130-150. Si la rejilla detectada se siente incorrecta en una prueba de captura de fotogramas, dividir/duplicar.
- La curva de energía encuentra el drop/estribillo: el mayor salto de RMS después de una sección tranquila.
2. Elegir la ventana de la canción
- Formato corto: 7-20 s. Iniciar la ventana en un downbeat y terminar en un downbeat o al final de una frase (4 u 8 compases).
- Colocar el visual más fuerte en el drop. Si el drop está a los 12 s de la canción y se desea a los 3 s del reel, la ventana comienza a los 9 s.
- Sonidos de tendencia en Instagram/TikTok: el sonido de la aplicación comienza en un punto fijo. Si la publicación final usará el sonido de la aplicación, alinea tu edición con ese punto de inicio y exporta una versión con el audio de referencia como guía.
- Desvanecer la música en 10-50 ms (evitar clic) a menos que la ventana comience en un transitorio, y fundirla durante 0.3-1.5 s, o terminar en un golpe fuerte.
3. Construir la rejilla y colocar eventos
fps = 30
fr = lambda t: round(t * fps) # ajustar cada evento al fotograma más cercano
U = beat_period / 2 # unidad sub-pulso (corcheas) si la canción es movida
- Cortes: en los pulsos para fluidez, en los golpes para impacto. Cortar en cada pulso es agotador; corta cada 2 o 4 pulsos y usa cortes en cada pulso solo para ráfagas (2-4 s) antes del drop.
- Anticipación: un corte colocado 1-2 fotogramas antes del pulso se siente más ajustado en teléfonos (latencia de audio + percepción). Prueba ambas opciones y elige de forma consistente.
- Destellos / sacudidas / zooms de énfasis: en los golpes, 2-6 fotogramas, con decaimiento.
- Texto: la palabra aparece en la sílaba vocal o en el pulso; mantén al menos 0.4 s por palabra para palabras individuales, ~0.25 s/palabra para líneas leídas al ritmo.
- Cambio de velocidad / efecto: en downbeats o drops.
- Mantén una tabla (
timing.json) de cada evento con su razón musical; el script de construcción la lee.
4. Rampas de velocidad (velocidad)
Una rampa de velocidad es un mapa de tiempo src_t = f(out_t). Se construye a partir de segmentos con velocidad suavizada:
# curva de velocidad: 1x ... rampa a 4x entre golpes ... vuelve a 0.3x en el golpe
segmentos = [(out_inicio, out_fin, velocidad_inicio, velocidad_fin, ease)]
- Suavizar la velocidad con smoothstep/cúbica de entrada y salida; un salto instantáneo 1x→4x parece un fallo.
- Lento (0.2-0.5x) en el golpe, rápido (2-5x) entre golpes. El propio fotograma del golpe muestra la acción clave (impacto, giro, mirada).
- Las secciones lentas necesitan fuentes de alta tasa de fotogramas o interpolación de fotogramas (
minterpolate, Resolve Speed Warp, RIFE). - Añadir desenfoque de movimiento en partes rápidas (
tmix=frames=3o mezcla de fotogramas en el renderizador del mapa de tiempo) para evitar parpadeo. - Caso real: toma aérea de un escritorio, partes con movimiento de cámara mantenidas a 1x, partes estáticas a 5.6x, cada cambio de velocidad en un golpe fuerte; un pase de flujo óptico (OpenCV LK + similaridad RANSAC) localizó los movimientos de cámara automáticamente (ver
motion-tracking).
timemap.py renderiza un mapa de tiempo extrayendo el fotograma fuente correcto para cada fotograma de salida (con mezcla opcional de fotogramas) — exacto y más simple que encadenar filtros setpts.
5. Coincidir con la sincronización de un reel de referencia
- Descargar/obtener la referencia; extraer su audio.
- Detectar sus cortes:
ffmpeg -i ref.mp4 -vf "select='gt(scene,0.3)',showinfo" -f null - 2>&1 | grep pts_time. - Detectar los pulsos en su audio; expresar cada corte como un índice de pulso o desplazamiento → esa es la plantilla de ritmo.
- Medir los tiempos de activación/desactivación de texto avanzando fotogramas (precisión 1/30 s) alrededor de los cambios.
- Reconstruir con tu material en la misma rejilla (ver
reference-matching).
6. Verificar
- Detectar escenas en el render; todo tiempo de corte × fps debe coincidir con su fotograma planificado (±0).
- Superponer una pista de clic en los pulsos en un render de depuración (
aevalsrcráfagas de seno) y capturar fotogramas en los golpes. - No puedes escuchar: reporta los números (fotogramas de corte planificados vs medidos) y pide al usuario que verifique auditivamente.