MÓDULO 2.2

🔧 Comportas, scripts e determinismo

Agora os comandos reais por trás de cada fase. Você vai ver os scripts do motorislands.py, cut.py, verify-cut.py, lint-timeline.py, captions.py, make-sfx.sh e mix-sfx.py — e a regra de determinismo que faz tudo isso ser confiável. Cada bloco é colável e vem com como verificar.

6
Tópicos
~35min
Duração
Intermediário
Nível
Prática
Tipo
Seções lidas neste módulo0% · 0 de 0
1

🎚️ silencedetect, não timestamps de Whisper

O corte não confia nos tempos que o Whisper devolve — eles têm um jitter de ±0,2–0,3s, o que deixa restos de silêncio e meias-palavras. Em vez disso, o motor mede o silêncio real do áudio com o silencedetect do ffmpeg. O script islands.py usa essa energia para achar as ilhotas de voz e propor quais manter, ficando com a última tomada de cada frase.

Objetivo

Detectar as ilhotas de voz do bruto por energia de áudio e gerar um islands.json com a proposta de KEEP/DROP — a base do corte, sem depender dos timestamps imprecisos da transcrição.

terminal
python3 islands.py \
  --media  \
  --transcript word.json \
  --noise -30dB --d 0.35 \
  --out islands.json

Como verificar

O script imprime uma tabela legível: cada ilhota com seu tempo, o texto, e o KEEP/DROP proposto com o motivo. Leia a tabela; se alguma tomada dobrada ou tangente escapou da heurística, corrija o campo "keep" no islands.json antes de seguir para o cut.py.

Conceitos-chave: silencedetect, jitter, ilhota de voz, KEEP/DROP.

2

✂️ islands.py → cut.py → verify-cut.py

O corte é uma cadeia de três scripts com uma comporta dura no fim. islands.py propõe o que manter; cut.py cola as ilhotas com keep=true num único arquivo; e verify-cut.py confere que o resultado está limpo — sem silêncios longos no corpo nem repetições. Ele sai com exit 0 quando passa e exit 1 quando falha. Você não anima nada antes desse PASS.

islands.py → islands.json cut.py cola keep=true → corte verify-cut.py exit 0 = PASSA revise keep comporta dura
A cadeia do corte: cada seta passa um artefato adiante. A última caixa (ciano) é bloqueante — se o verify-cut.py não der exit 0, o corte não avança para as animações.
terminal
# 1) monta o corte a partir das ilhotas com keep=true
python3 cut.py --islands islands.json --out edicion/corte-final.mp4

# 2) re-transcreva o CORTE (word-level) e verifique — bloqueante
python3 verify-cut.py \
  --media edicion/corte-final.mp4 \
  --transcript corte-word.json \
  --max-sil 0.6
echo "exit: $?"   # 0 = PASSA · 1 = FALHA

Como verificar

Olhe o código de saída (echo "exit: $?"). 0 significa corte limpo — pode animar. 1 significa que ainda há silêncio longo no corpo ou uma repetição audível: o script imprime exatamente o que falhou. Conserte os keep e rode a cadeia de novo até dar 0.

Conceitos-chave: cadeia de scripts, comporta dura, exit 0/1.

3

📏 lint-timeline.py: gap > 4s = erro

A regra de ritmo "nunca mais de 4s sem golpe visual" deixa de ser um conselho e vira verificação automática. O lint-timeline.py lê o motion/index.html do Hyperframes, mede a distância entre os beats visuais e acusa erro quando encontra um buraco maior que o --max-gap (4s por padrão). É uma rede de segurança do ritmo — não substitui olhar os frames, mas pega o furo óbvio antes de renderizar.

Objetivo

Rodar um lint estático na timeline e falhar (exit ≠ 0) se houver qualquer trecho com mais de 4s sem um beat visual — bloqueando o render de um reel com buraco de ritmo.

terminal
# lint do ritmo: falha se houver >4s sem beat visual
python3 lint-timeline.py motion/index.html --max-gap 4.0
echo "exit: $?"   # 0 = ritmo ok · ≠0 = buraco de ritmo

Como verificar

Se passar, exit 0 e silêncio. Se falhar, o lint aponta a linha e o intervalo do buraco ("gap de X s a partir de Ys"). Adicione um beat (corte, zoom, chip, B-roll ou reveal) naquele ponto do motion/index.html e rode de novo até zerar os erros.

Conceitos-chave: lint estático, beat visual, --max-gap, comporta de ritmo.

4

💬 captions.py: legenda na altura do micro

O captions.py gera "beats" de legenda de 2–3 palavras, sincronizados com a voz, no estilo retenção. O truque anti-clichê está no destaque: as palavras que você passa em --keywords (números, nomes de marca, conceitos fortes) são pintadas na sua cor de acento. Como as keywords mudam a cada vídeo, suas legendas nunca saem iguais às de ninguém.

Objetivo

Produzir um captions.json com beats curtos e uma palavra-chave destacada por beat, para legendas que apoiam a fala (na altura do peito/microfone) em vez de repeti-la embaixo da tela.

terminal
python3 captions.py \
  --transcript corte-final.json \
  --max-words 3 \
  --keywords "" \
  --out captions.json

Como verificar

Abra o captions.json: cada beat tem start, end e uma lista words com hi:true na palavra destacada. Confira que os tempos batem com a voz e que a palavra em destaque é a certa (se não houver match nas keywords, o script destaca a palavra mais longa do beat).

Conceitos-chave: beat de legenda, --keywords, cor de acento, área segura.

5

🔊 make-sfx.sh + mix-sfx.py: SFX sob a voz

O som é feito em dois passos. make-sfx.sh sintetiza a paleta de efeitos (whoosh, pop, type, buzz, boom, ding, riser) com o ffmpeg — sem baixar nada, livre de direitos e sempre igual. Depois, mix-sfx.py sobrepõe cada efeito no tempo certo por baixo da voz, com um limitador para não saturar. Você passa os eventos como uma lista de [nome, segundo].

Objetivo

Gerar a paleta de SFX localmente e misturá-la no render nos tempos dos cortes, entregando um final.mp4 com efeitos que "produzem" as transições sem competir com a fala.

terminal
# 1) sintetiza a paleta de SFX em ./sfx
bash make-sfx.sh sfx

# 2) mistura os efeitos sobre o render, por baixo da voz
python3 mix-sfx.py \
  --base render.mp4 --sfx-dir sfx --out final.mp4 \
  --events '[["boom",0.0],["whoosh",6.5],["ding",8.6]]'

Como verificar

Depois do make-sfx.sh, confira que a pasta sfx/ tem os .wav (whoosh, boom, ding…). Depois do mix-sfx.py, ouça o final.mp4: os efeitos devem cair exatamente nos cortes e ficar por baixo da voz. Se você re-cortar o vídeo, os tempos dos eventos mudam — re-temporize a lista --events.

Conceitos-chave: paleta de SFX, eventos [nome, t], ducking, limitador.

6

🎲 Determinismo em Hyperframes

Nada disso funciona se cada render sair diferente. Por isso as animações do Hyperframes são determinísticas: sem Math.random() e sem Date.now(), nada de repeat:-1 (repetições infinitas), e as timelines ficam em estado paused, registradas para o motor controlar frame a frame. Mesmo projeto → mesmo frame, toda vez — condição para o lint e o QC serem confiáveis.

✗ Quebra o determinismo

  • Math.random() para posições/tempos
  • Date.now() / relógio do sistema
  • repeat:-1 (loop infinito)

✓ Mantém o determinismo

  • Valores fixos ou derivados da duração
  • Repetições finitas calculadas
  • gsap.timeline({paused:true}) registrada

Objetivo

Confirmar que o motor de motion (Hyperframes) está instalado e disponível — o pré-requisito para renderizar as timelines determinísticas.

terminal
# confere que o motor de motion está disponível
npx hyperframes --version

# checagem rápida: nenhuma fonte de aleatoriedade na timeline
grep -nE "Math\.random|Date\.now|repeat:\s*-1" motion/index.html || echo "OK: determinístico"

Como verificar

O --version deve imprimir um número (motor instalado). O grep deve imprimir OK: determinístico: se ele listar linhas, há uma fonte de aleatoriedade que precisa sair antes de renderizar. Rode o lint-timeline.py logo depois — os dois juntos são a garantia de um render reprodutível.

Conceitos-chave: determinismo, repeat finito, paused, reprodutibilidade.

Resumo do módulo

Corte por silêncio real — islands.py acha as ilhotas por energia, não pelos timestamps do Whisper.
Cadeia com comporta dura — cut.py monta e verify-cut.py bloqueia (exit 0/1) antes de animar.
Ritmo e legendas verificados — lint-timeline.py trava gaps >4s; captions.py destaca suas keywords.
SFX local e render determinístico — make-sfx.sh/mix-sfx.py e Hyperframes sem random/now: mesma entrada, mesma saída.

Próxima trilha:

Trilha 3 — Como usar: instalar a stack, gerar a sua skill na entrevista e rodar o primeiro reel.