# Som e trilha

Dois trabalhos, nesta ordem:

1. **Efeitos sonoros** casados com o que aparece na tela: `helpers/plano_sfx.py`.
2. **Trilha** contínua por baixo da voz, no fim de tudo: `helpers/mixar_trilha.py`.

Rode os helpers com o Python desta skill (tem numpy): `uv run --directory "<SKILL>" python helpers/<nome>.py`.
Precisam do ffmpeg. Com o `--directory` o comando roda dentro da pasta da skill: passe cada arquivo com o caminho completo, entre aspas.

## O caminho no projeto (um só)

No projeto-modelo (`<PROJ>`, a cópia do `modelo-projeto`), os sons vão pelo `projeto.json`. Nunca cole tag
`<audio>` no `index.html`: o build reescreve o arquivo a cada vez.

1. `cd '<PROJ>' && python3 build.py completo`: o vídeo com as animações, **sem trilha**. O build grava
   `sons_sugeridos.json` com o som que cada animação, zoom, troca de cena e entrada de prova pede, com o tempo do
   PICO (o quadro em que o movimento bate). As cenas próprias não entram sozinhas: anote o quadro de cada
   mudança num `<PROJ>/eventos.json` (`[{"t": 12.4, "tipo": "pop"}]`).
2. `uv run --directory "<SKILL>" python helpers/plano_sfx.py --projeto "<PROJ>"`: lê o `sons_sugeridos.json`
   (e o `eventos.json`), monta o plano (um som por animação, espaço mínimo de 0,65 s, fica o mais forte), mede a
   voz no `cut.mp4`, copia os sons para `<PROJ>/assets/sfx` (com `LICENCAS.txt`) e escreve o campo `sons` do
   `projeto.json`: arquivo, `em` (o pico), `pico_no_arquivo`, `inicio_no_arquivo` (quando o pico vem antes do
   começo do vídeo, só a cabeça do som sai), `dur`, `volume` e `fade`. Grava também `<PROJ>/plano_sfx.json`.
3. `python3 build.py completo` de novo: os sons entram no `index.html`, cada um na sua faixa. O estúdio toca
   todos; confira ali com som. O build nunca descarta um som calado: o que não cabe sai com `aviso:`.
4. Render: `python3 build.py hf render --quality delivery --output renders/final.mp4`. Os sons já vêm no arquivo.
5. Corte seco, se precisar (`references/corte-seco.md`). Ele lê o `plano_sfx.json` pra não cortar em cima de
   um efeito.
6. Trilha por último, no arquivo renderizado (e já cortado): `helpers/mixar_trilha.py`. Assim a música não pula
   nas emendas.

Se a trilha entrar antes do corte seco, a música pula em cada emenda e o corte seco deixa de enxergar as pausas.

Mudou uma animação de lugar? Rode o `completo`, o `plano_sfx.py --projeto` e o `completo` de novo: o plano troca
só os sons que ele mesmo escreveu (`"plano": true`); som escrito à mão no `projeto.json` fica. Com o
`sons_sugeridos.json` de uma amostra, ele troca só os sons daquele trecho e avisa.

Fora do projeto-modelo (vídeo já pronto, sem HyperFrames): monte o plano com um `eventos.json` e junte os sons
por cima com `--aplicar` (seção abaixo).

## Efeitos sonoros

### A regra

- **Vídeo com animação**: cada animação que importa ganha o seu som, combinando com o movimento. Pop no pop, whoosh no deslize, impacto no carimbo, ding na notificação. O pico do som cai no quadro em que o movimento bate, nunca na palavra. Não tem teto de quantidade (o André aprovou 23 sons em 56 s): quem tira é o espaço mínimo.
- **Vídeo sem animação** (só a pessoa falando): uns 5 ou 6 sons no vídeo inteiro de 1 a 1min30. Um na abertura, um no maior número de dinheiro (e no segundo maior, se estiver longe), um em cada grande virada de assunto (no máximo 2) e um na chamada.
- Nunca som em cada palavra, nem em cada item de lista sem animação.
- Espaço mínimo de 0,65 s entre dois sons. Se dois caem perto, fica o mais forte.
- Os pequenos ficam baixinhos. Só os momentos-chave ficam altos.

Tabela completa, níveis e quando **não** pôr som: `references/tabela-de-sons.md`.

### Passo a passo

1. **Liste os eventos visuais** com o tempo do quadro em que cada movimento bate: o pouso do pop (fim do crescimento), o meio do deslize, o impacto do carimbo, o instante em que a notificação aparece. No projeto-modelo, o `sons_sugeridos.json` já faz isso para as peças do catálogo; você só escreve os das cenas próprias. Pegue esse tempo do código da animação (início do movimento + tempo até o pouso), nunca do começo da palavra. Salve em `eventos.json` (modelo em `references/exemplo-eventos.json`):

   ```json
   [{"t": 1.20, "tipo": "pop"},
    {"t": 3.40, "tipo": "impacto", "nivel": "destaque"},
    {"t": 7.64, "tipo": "whoosh-grande", "forcar": true, "nota": "tela cheia vira moldura"}]
   ```

2. **Monte o plano**:

   ```bash
   uv run --directory "<SKILL>" python helpers/plano_sfx.py --projeto "<PROJ>"                  # projeto-modelo
   uv run --directory "<SKILL>" python helpers/plano_sfx.py eventos.json --video final.mp4      # vídeo pronto
   ```

   Ele lista os sons que ficaram, os que saíram e o motivo, e confere onde o pico caiu. O desvio tem que ficar abaixo de 1 quadro (33 ms a 30 fps). Com `--video` (ou, no `--projeto`, o `cut.mp4` do edit), ele também acerta os níveis pela voz do vídeo.

3. **Coloque os sons**, de um destes jeitos:
   - **No projeto-modelo**: o `--projeto` já escreveu o campo `sons` do `projeto.json`. Rode `python3 build.py completo` e o render.
   - **No vídeo pronto**: `--aplicar final.mp4 --video-saida final_sons.mp4`. A voz fica no lugar e o volume final vai pra -14 LUFS.

4. **Guarde o `plano_sfx.json`**. O corte seco usa ele.

Detalhes do plano:

- `--tipos` lista os tipos e os sons de cada um. Apelidos em português funcionam ("aparece", "deslize", "carimbo", "riscar", "moedas").
- Som específico num evento: `"som": "woven:ID"` (`assets/sfx/woven` desta skill), `"kit:NOME"` (kit de `assets/sfx` desta skill), `"bib:categoria:nome"` (biblioteca de 445 sons CC0 em `~/.editor-video/efeitos`, ex. `bib:impacto:boom`) ou um caminho de arquivo.
- `"nivel"`: `destaque`, `medio`, `discreto` ou um número em dBFS de pico.
- `"forcar": true`: o som fica mesmo perto de outro e mesmo passando do máximo. Use em momento-chave e pra disfarçar uma emenda de corte visível.
- `"dur"`: quanto tempo toca (digitação, aplauso).
- `--modo motion` (padrão: um som por animação; só o espaço mínimo tira) ou `--modo fala` (sem animação, 1 a cada 15 s). `--max N` põe um teto. `--espaco 0.7` troca o espaço mínimo. `--variar` alterna os sons do mesmo tipo.
- Mais de um arquivo de eventos: `plano_sfx.py sons_sugeridos.json eventos.json --projeto "<PROJ>"` (sem arquivo, o `--projeto` lê os dois da pasta).
- O `--html` antigo saiu: ele gravava um `sfx_audio.html` na raiz do projeto (dois erros no lint do estúdio) para colar no `index.html`, que o build reescreve.
- `--medir SOM` mostra duração, pico e começo de qualquer som.

## Trilha

### A regra

- A trilha fica **22 dB abaixo da voz** (padrão). A faixa boa é de 20 a 24. Com 28 ou mais ela some no celular.
- Sempre medida contra a voz, em LUFS. Um número de ganho sozinho ("-40 dB") não diz nada, porque depende de quão alta a música veio.
- A trilha abaixa quando a pessoa fala (duck) e volta nas pausas longas e no final.
- Entra contínua por cima do vídeo já cortado.
- "A música sumiu": suba 3 dB (`--abaixo-da-voz 19`). "Tá competindo com a minha voz": desça 3 (`--abaixo-da-voz 25`).

### Passo a passo

```bash
uv run --directory "<SKILL>" python helpers/mixar_trilha.py --video final_seco.mp4 --trilha musica.wav \
    --abaixo-da-voz 22 --saida final_trilha.mp4
```

Opções:

- `--entrada-em 12.0`: a música entra no segundo 12 do vídeo (ex.: quando a pessoa fala "trilha sonora").
- `--respiro` (ou `--respiro 3`): a música entra mais alta por 2 s (ou 3), 10 dB acima (`--respiro-db`), e depois assenta. Bom na entrada da música.
- `--duck 8`: quanto ela abaixa na fala, no máximo.
- `--fade-fim 1.5`: fade no fim. `--inicio-trilha 30`: começa a música do segundo 30 dela.
- Música mais curta que o vídeo repete sozinha, com crossfade de 1 s.

A resposta traz: quantos dB a trilha ficou abaixo da voz, quanto o duck abaixou, a duração da imagem e do som (têm que bater com a entrada), o desvio da voz (tem que dar 0 ms) e o volume final (-14 LUFS). Os números ficam também em `SAIDA.trilha.json`.

### Onde achar trilha

- Biblioteca de trilhas desta skill em `~/.editor-video/trilhas`, por humor (animada, calma, épica, inspiradora...). Liste e escolha com o `helpers/trilhas.py`. São de uso próprio da pessoa: não redistribua o arquivo.
- Música que a pessoa mandar: pergunte se ela tem direito de usar em anúncio.

## Como falar com a pessoa

- Antes: "Vou pôr um som em cada animação, baixinho, e os mais fortes só nos momentos importantes. A música fica por baixo da sua voz e abaixa quando você fala."
- Depois: diga quantos sons entraram, quais momentos têm som forte e quantos dB a música ficou abaixo da voz. Uma linha cada.
- Ofereça um ajuste por vez: "quer a música um pouco mais alta?", "quer tirar algum som?".
- Pedido pra tirar um som: apague o evento (ou mude pra `"nivel": "discreto"`) e rode de novo. Não mexa no resto.

## Pegadinhas conhecidas

- **HyperFrames**: quando o `<audio>` tem `data-automation` com lane de volume, o `data-volume` é ignorado. O build não usa lane: o ganho vai no `data-volume` (no máximo 3,98, +12 dB) e as descidas em `data-fade-out`/`data-fade-in`.
- **HyperFrames**: cada som com o seu `data-track-index`. O build numera.
- **Som cujo pico vem antes do começo do vídeo** (um clique logo no quadro 0): o build começa o som no 0 e pula a cabeça do arquivo (`data-media-start`), com uma subida de 10 ms. Nunca some.
- **ffmpeg 8**: o `sidechaincompress` corta a saída quando uma das entradas acaba antes. O `mixar_trilha.py` passa todas as entradas por `apad` e `atrim` na duração exata do vídeo.
- **Som com vários picos parecidos** (glitch, risco de disco, impacto saturado): o "pico" usado é o começo do trecho mais forte (a 1 dB do máximo). É onde o ouvido sente a batida.
- **Pico real depois do AAC**: o codificador pode passar do teto em até 2 dB. Os scripts medem depois de gravar e, se passou de -0,5 dBTP, gravam de novo com um limitador leve só nos picos. O volume integrado continua -14 LUFS.
- **Trilha que já veio mixada no vídeo**: não dá pra tirar. Renderize de novo sem trilha.

## Arquivos

- `helpers/plano_sfx.py`: plano de sons, `--projeto`, `--aplicar`, `--tipos`, `--medir`.
- `helpers/mixar_trilha.py`: trilha com duck, medida contra a voz.
- `references/tabela-de-sons.md`: tipo de animação e som, densidade, níveis em dB, quando não pôr som, medidas de cada arquivo.
- `references/exemplo-eventos.json`: modelo de eventos.
- `assets/sfx/woven/`: 35 sons do Woven SFX (CC0, domínio público). Licença em `assets/sfx/woven/LICENCA.md`.
