Edição e Masterização de Podcast
Processa gravações de podcast ou narração do usuário para criar um arquivo final pronto para publicação: redução de ruído, remoção de silêncios nas bordas, equalização e compressão vocal, normalização de loudness para aproximadamente -16 LUFS e exportação em MP3. Quando necessário, adiciona música de abertura, encerramento e segmentada, além de fornecer sugestões de edição (quais pausas longas podem ser encurtadas e onde estão os trechos regravados), que o usuário confirma antes de aplicar os cortes.
Os diretórios scripts/ e references/ estão dentro do diretório desta skill. Ao executar, use o caminho completo, por exemplo: bash <caminho_desta_skill>/scripts/edit_podcast.sh.
1. Antes de começar, esclareça com o usuário
- É necessário ter o ffmpeg instalado localmente (ferramenta de áudio gratuita e de código aberto). Se não estiver instalado, peça ao usuário para instalar: no macOS com
brew install ffmpeg, no Linux com o gerenciador de pacotes (ex.:apt install ffmpeg). O script procura nesta ordem: variável de ambienteFFMPEG→ffmpegno PATH do sistema → locais comuns de instalação como/opt/homebrew/bine/usr/local/bin. Testado com as versões 6.0 e 8.1. - Requer bash e Python 3.9 ou superior, usando apenas a biblioteca padrão (sem necessidade de instalar pacotes Python adicionais).
- Suporta apenas macOS e Linux: testado no macOS; no Linux deve funcionar; no Windows é necessário WSL (não testado).
- Na primeira redução de ruído, será baixado um modelo público de redução de ruído (RNNoise) de aproximadamente 300 KB da internet, armazenado em
~/.cache/ethan-podcast-post/, com verificação SHA-256 após o download. Se não for possível baixar ou a verificação falhar, o script usará automaticamente oafftdnnativo do ffmpeg. O script informará qual método está sendo usado; você deve comunicar isso ao usuário. A origem do modelo está emreferences/third-party.md. - Todo o processamento é feito localmente: a gravação não é enviada para nenhum servidor e não há cobrança.
- O que esta skill não faz: não gera ou clona vozes, não dubla, não faz transcrição (se precisar de legendas, o usuário deve fornecer um arquivo SRT), não publica em nenhuma plataforma, não fornece música (a música de fundo deve ser fornecida pelo usuário, que deve confirmar que tem os direitos de uso).
2. Segurança dos arquivos (obrigatório)
- Gravação original somente leitura: o script apenas lê o arquivo, não o modifica, move ou exclui.
- Saída sempre em um novo diretório: crie um novo diretório ao lado da gravação (ex.:
Episodio3_pos-producao/) e não o coloque no mesmo diretório da gravação ou da música de fundo. O script para se o diretório de saída for o mesmo do arquivo de entrada. - Não sobrescrever sem permissão: se o diretório de saída já contiver o arquivo final,
audio_raw.wavouvoice_master.wav, o script para e não sobrescreve. Para sobrescrever, informe ao usuário quais arquivos serão substituídos e que os antigos não poderão ser recuperados. Com a concordância do usuário, execute novamente comOVERWRITE=1. O mesmo vale para sugestões de edição, cortes e processamento de voz: se o arquivo de saída já existir, o script para; o usuário deve concordar antes de usar--overwrite. - Confirmar antes de excluir: qualquer exclusão (incluindo limpeza de arquivos grandes como
audio_raw.wav) deve ser comunicada ao usuário (qual arquivo, por que) e só realizada após sua autorização. Os arquivos intermediários do script ficam em um subdiretório temporário dentro do diretório de saída, e apenas este subdiretório é limpo ao final. - O que cortar é decidido pelo usuário: as sugestões de edição são apenas uma lista de candidatos; nenhum corte é aplicado sem a confirmação do usuário.
3. Fluxo de processamento
Execute uma verificação do ambiente e informe o resultado ao usuário:
bash <caminho_desta_skill>/scripts/edit_podcast.sh --check
Isso listará o ffmpeg encontrado, sua versão, se a equalização dinâmica de 250 Hz está habilitada (veja seção 6) e se o modelo de redução de ruído está pronto. Se o ffmpeg não for encontrado, uma mensagem indicará como instalá-lo; instale antes de prosseguir.
Passo 1: Primeira passagem – produzir master vocal e versão de prova
KEEP_MASTER=1 bash <caminho_desta_skill>/scripts/edit_podcast.sh <arquivo_de_audio> <diretorio_saida>/pass1 <nome>
- O áudio pode ser wav, m4a, mp3, mp4 ou qualquer formato que o ffmpeg leia. Para conversas com duas ou mais pessoas, adicione
INTERVIEW_MODE=1(compressão mais forte e equalização automática do volume entre os participantes). - Ordem do processamento: extrair para WAV 48 kHz → verificação de qualidade → remoção de silêncios nas bordas (pausas internas não são tocadas) → remoção de cliques labiais e dentais → filtro passa-alta de 80 Hz → redução de ruído → equalização dinâmica de 250 Hz → de-esser → compressão → realce de 5 kHz → ganho linear para -16 LUFS → limitador → exportação MP3 → nova medição de loudness.
- Saídas:
<nome>_final.mp3: arquivo final sem música (também serve como versão de prova);voice_master.wav: master vocal (já com silêncios das bordas removidos) – usado para transcrição, sugestões de edição e cortes;audio_raw.wav: cópia da gravação original convertida para WAV (escrita no diretório de saída somente após todo o processamento bem-sucedido) – útil para refazer o processo; arquivo grande.
- Verifique os seguintes itens na saída e informe ao usuário:
- Volume da cauda abaixo de -80 dB: a gravação pode ter sido interrompida no meio; confirme com o usuário;
- Método de redução de ruído: RNNoise ou afftdn;
- A loudness do arquivo final deve estar dentro de -16 LUFS ±1, e o pico verdadeiro não deve exceder -1,5 dBTP. Se os valores estiverem fora, o script emitirá um aviso; informe o usuário.
- Se a gravação estiver muito baixa ou for silenciosa, o script emitirá um erro e parará, sem gerar um arquivo final vazio. A mensagem de erro incluirá um valor sugerido para
INPUT_GAINcom base no pico da gravação: primeiro confirme com o usuário que há fala na gravação e, em seguida, execute novamente comINPUT_GAIN=<valor_sugerido>(em dB, já considerando o `INPUT_GAIN atual) para elevar o volume geral. O diretório de saída não conterá arquivos após o erro, portanto o mesmo diretório pode ser reutilizado. Aproveite para sugerir ao usuário que aumente o volume de entrada nas gravações futuras.
Passo 2: Sugestões de edição (apenas lista de candidatos – nenhum áudio é modificado)
A transcrição deve ser feita a partir do voice_master.wav do passo 1 (ou do MP3 final do passo 1 sem música, pois ambos têm a mesma linha do tempo). Não use a transcrição da gravação original: os silêncios das bordas não foram removidos no original, portanto os tempos não coincidirão e as marcações ficarão incorretas.
Encontrar pausas longas que podem ser encurtadas:
python3 <caminho_desta_skill>/scripts/edit_cuts.py suggest <diretorio_saida>/pass1/voice_master.wav \
[--srt legenda.srt] [--protect 15:00.0-15:03.0 ...]
- Por padrão, pausas com mais de 1,5 segundos são listadas como candidatas, com sugestão de encurtar para 1,2 segundos (mantendo metade do silêncio de cada lado para preservar o ruído ambiente).
- Pausas intencionais do usuário (ex.: silêncio após uma frase importante) podem ser protegidas com
--protect inicio-fim, deixando 0,5 segundos de margem em cada lado; elas não serão sugeridas para corte. - Se pausas longas óbvias não forem encontradas, o ruído de fundo pode estar alto: ajuste
--noisede -45 para -40 e execute novamente. - Se houver mais de 15 candidatos ou um total acumulado de mais de 60 segundos a cada 40 minutos de áudio, o script sugerirá que os parâmetros estão muito agressivos: aumente
--min-pausee não corte todos os candidatos.
Encontrar trechos de regravação marcados por uma palavra-chave (requer legenda):
python3 <caminho_desta_skill>/scripts/edit_cuts.py retakes <diretorio_saida>/pass1/voice_master.wav --srt legenda.srt [--phrase sua_palavra_chave]
- Convenção durante a gravação: quando algo é dito errado → pausa de 1 segundo ou mais → a palavra-chave de regravação é dita calmamente → pausa de 1 segundo ou mais → a frase é dita novamente do início.
- A palavra-chave padrão é "de novo". Para usar sua própria palavra-chave, use
--phrase. Se o software de transcrição frequentemente escrever a palavra-chave com homófonos, use--phrasevárias vezes com as diferentes grafias. Escolha uma palavra-chave que não apareça no corpo do texto; repeti-la duas vezes torna a detecção mais precisa. - O script procura a palavra-chave na legenda, encontra o início da nova tentativa e tenta identificar a frase errada anterior, fornecendo o intervalo do corte e um nível de confiança (high / mid / low). Candidatos com confiança "low" devem ser ouvidos pelo usuário para confirmação.
Os dois comandos geram, respectivamente, voice_master_suggested_cuts.tsv e voice_master_retake_cuts.tsv (não sobrescrevem se já existirem; use --overwrite com a autorização do usuário). Apresente cada candidato ao usuário (tempo, contexto, quantos segundos podem ser economizados). Após o usuário confirmar quais cortes deseja, copie as linhas correspondentes para um novo arquivo cuts.tsv (separado por tabulações: início, fim, observação; tempo no formato MM:SS.s).
Passo 3: Aplicar cortes
python3 <caminho_desta_skill>/scripts/edit_cuts.py apply <diretorio_saida>/pass1/voice_master.wav --cuts cuts.tsv [--srt legenda.srt]
- Os cortes são aplicados com precisão de amostra, com crossfade de potência igual de 15 milissegundos nas emendas. Gera
voice_master_edited.wavem 24 bits e o mapa de ediçãovoice_master_edited.edit_map.json(registra a origem e posição de cada segmento mantido). - Se
--srtfor fornecido, as legendas completas serão deslocadas para corresponderem ao áudio editado. Se alguma legenda for cortada ao meio, o script se recusará a executar – execute sem--srte, após o corte, peça ao usuário para gerar uma nova transcrição a partir do master editado. - Após o corte, peça ao usuário para ouvir alguns segundos antes e depois de cada emenda: não deve haver cliques, duas inspirações consecutivas ou mudanças bruscas no timbre.
Passo 4 (opcional): Aplicar textura sonora diferenciada em trechos (citações, memórias, etc.)
python3 <caminho_desta_skill>/scripts/voice_fx.py <master_editado.wav> --fx fx.tsv
python3 <caminho_desta_skill>/scripts/voice_fx.py --list # Ver os cinco presets
- Presets: quote (leitura de cartas, textos de outras pessoas), archive (jornais antigos, documentos oficiais), telephone (relato por telefone), memory (recordação, sonho), radio (rádio antigo – no máximo uma vez por episódio).
- fx.tsv: cada linha contém preset, início, fim, fade-in, fade-out, observação (separados por tabulação; use "-" para valores padrão de fade).
- O processamento não altera a duração nem a loudness; a saída é
<nome_original>_fx.wav.
Passo 5 (opcional): Música e tabela de cues
Só deve ser feito se o usuário fornecer música de fundo. Primeiro, crie a tabela de cues; depois, faça a pré-verificação. A tabela de cues deve estar no diretório de saída da segunda passagem e o nome do arquivo deve ser <nome>_cues.tsv.
Tabela de cues: 7 colunas separadas por tabulação; linhas iniciadas com # são ignoradas; use "-" para usar o valor padrão. time no formato MM:SS.s, relativo à linha do tempo do master vocal. Os exemplos abaixo são ilustrativos:
# type time src dur vol fade_in fade_out
stinger 02:00.0 - 3 - - -
break 12:00.0 -@20 8 0.5 2 2
bed 18:00.0 -@40 30 0.08 - -
ending 40:00.0 -@90 - - - -
| Tipo | Função | dur | Volume padrão |
|---|---|---|---|
| stinger | Um pequeno trecho musical, sobreposto à fala | Padrão 6 s | 0,12 |
| bed / post | Música de fundo; quando a fala aparece, o volume é automaticamente reduzido | Obrigatório | 0,12 |
| break | Neste ponto, a fala é interrompida para inserir um trecho de música pura; após o fim da música, a fala retorna. O arquivo final fica dur segundos mais longo | Obrigatório | 0,5 |
| ending | Música de encerramento: volume reduzido durante a fala, sobe após a fala terminar e faz fade-out no final | Use "-" | 0,22 |
srccom "-" significa usar a música de fundo fornecida nesta execução;-@20significa iniciar a partir do segundo 20 da música de fundo; também é possível usar o caminho completo de outro arquivo de música, com ou sem@segundo(sem@, começa do início).- Para stinger, se
srcfor "-" sem@, o trecho será retirado a partir do segundo 60 da música de fundo; para outros tipos, sem@, começa do início. - Após criar a tabela, faça a pré-verificação. Corrija qualquer ❌ antes de prosseguir:
python3 <caminho_desta_skill>/scripts/cues_lint.py <nome>_cues.tsv --master <master_editado.wav> --bg <musica_de_fundo>
Isso verificará formato, limites, existência dos arquivos de áudio, sobreposição de janelas, proporção total de música (recomendado não ultrapassar 15%) e fornecerá uma tabela de conversão "master → arquivo final" para facilitar a edição da linha do tempo do programa.
Passo 6: Segunda passagem – gerar o arquivo final
MASTER_WAV=<master_editado.wav> bash <caminho_desta_skill>/scripts/edit_podcast.sh - [musica_de_fundo] <diretorio_saida>/final <nome>
- O primeiro argumento é "-". Com
MASTER_WAVdefinido, o script pula a extração, redução de ruído e normalização, aplicando diretamente os cues, mixando a música e exportando. - Se houver música de fundo e o áudio for mais longo que 80 segundos, a abertura e o encerramento são adicionados automaticamente: a música de abertura toca por 3 segundos antes da fala entrar; após a fala terminar, a música continua por cerca de 7 segundos com fade-out; o volume da música é automaticamente reduzido quando a fala aparece.
- Se não houver cortes nem música, o arquivo final do passo 1 já é o resultado final; este passo não é necessário.
Passo 7: Verificação final
O script já faz uma medição ao final. Para uma medição independente adicional (use o caminho do ffmpeg exibido em --check):
"<caminho_do_ffmpeg>" -hide_banner -nostats -i <arquivo_final.mp3> -filter_complex ebur128=peak=true -f null - 2>&1 | tail -12
- Critérios de aceitação: Integrated entre -16 LUFS ±1, True peak não superior a -1,5 dBFS (ou dBTP).
- Peça ao usuário para ouvir com fones de ouvido: a música não deve abafar a fala; a música não deve retornar abruptamente durante pausas da fala; cada ponto de corte deve soar natural; o final não deve ser cortado abruptamente; as pausas intencionais devem permanecer.
- Ao entregar, informe ao usuário: local do arquivo final, método de redução de ruído, valores medidos de loudness e pico verdadeiro, e quais cortes foram aplicados. Se os critérios não forem atendidos, informe honestamente, sem dizer que passaram.
4. Princípios de edição
- Corte apenas acidentes, não respirações: cortes são para erros de fala, leitura do roteiro com distração, trechos regravados descartados. Respirações, "hmm" de pensamento, hesitações entre frases e silêncios intencionais devem ser mantidos. Em caso de dúvida, mantenha.
- Pausas de até 0,8 segundo não são alteradas; pausas entre frases de 0,8 a 1,5 segundo são mantidas por padrão; pausas mais longas só devem ser encurtadas se houver evidência de hesitação ou distração.
- Os pontos de corte devem ser escolhidos em locais de pausa natural para respiração, sem cortar o início ou o final de palavras. Respirações devem ser mantidas ou removidas por completo.
- A música de fundo deve ficar abaixo da fala, evitando músicas com vocais ou com muita energia na faixa média (1-3 kHz). Músicas com melodia são preferencialmente colocadas entre parágrafos (break), não sobrepostas à fala. A duração total da música geralmente não deve ultrapassar 15% do arquivo final.
- Para detalhes, consulte
references/editing-style-guide.md(estilo e apresentação) ereferences/editing-essentials.md(execução da edição e verificação). Consultereferences/audio-glossary.mdpara termos técnicos.
5. Variáveis de ambiente (todas opcionais)
| Variável | Padrão | Função |
|---|---|---|
FFMPEG | Busca automática | Caminho do ffmpeg |
DENOISE | rnnoise | rnnoise / afftdn / off |
RNNOISE_MODEL | ~/.cache/ethan-podcast-post/bd.rnnn | Localização do modelo de redução de ruído; se já existir, pode apontar diretamente para ele |
RNNOISE_URL | Modelo público no GitHub | URL alternativa para download (a verificação SHA-256 ainda será feita) |
INPUT_GAIN | 0 | Aumentar (ou diminuir) o volume geral da gravação antes do processamento (dB); use o valor sugerido no erro caso a gravação esteja muito baixa |
TRIM_THRESHOLD | -50 | Limiar (dB) para remoção de silêncios nas bordas; se sons suaves no início/fim forem cortados, reduza o valor (ex.: -60) |
INTERVIEW_MODE | 0 | 1 = conversa com duas ou mais pessoas |
KEEP_MASTER | 0 | 1 = manter o master vocal voice_master.wav |
MASTER_WAV | vazio | Caminho do master vocal para a segunda passagem |
MUSIC_VOLUME | 0,12 | Proporção de volume da música de fundo |
INTRO_DURATION | 40 | Janela (em segundos) para música de abertura e encerramento; se o áudio for mais curto que o dobro deste valor, a música não é adicionada |
PREROLL / TAIL | 3 / 7 | Segundos de música antes da fala / segundos de música após a fala terminar |
OUTRO_SEEK | vazio | A partir de qual segundo da música de fundo começar a música de encerramento |
MP3_BITRATE | 160k | Taxa de bits do MP3 final |
OVERWRITE | 0 | 1 = permitir sobrescrita de arquivos existentes (após obter consentimento do usuário) |
6. Perguntas frequentes
- ffmpeg não encontrado: peça ao usuário para instalar (macOS:
brew install ffmpeg; Linux: gerenciador de pacotes) ou especifique o caminho comFFMPEG=/caminho/completo/ffmpeg. - Versão do ffmpeg: o script lê o número da versão. O significado dos parâmetros da equalização dinâmica de 250 Hz mudou entre o ffmpeg 6 e 8; ambas as implementações foram calibradas com um sinal senoidal e produzem o mesmo resultado. Para versões não testadas (ex.: 7.x), esta etapa será ignorada com um aviso, mas o restante do processamento continua normalmente. Os parâmetros de remoção de silêncios das bordas também foram ajustados para o comportamento das versões recentes; 6.x e 8.x cortam de forma idêntica.
- Erro "após remover silêncios das bordas, quase nada restou" ou "loudness não pôde ser medida": a gravação está muito baixa ou é silenciosa. Confirme se há fala e execute novamente com o valor de
INPUT_GAINsugerido no erro (quando a loudness está abaixo de -70 LUFS, não é possível medir; apenas ajustar o limiar de corte não é suficiente). Se a gravação realmente não tiver som, o erro persistirá e nenhum arquivo vazio será gerado. - Redução de ruído usando afftdn: significa que o modelo RNNoise não foi baixado com sucesso. Defina
RNNOISE_URLpara um espelho alternativo ou useRNNOISE_MODELpara apontar para um arquivo de modelo existente e execute novamente (pergunte ao usuário antes de sobrescrever arquivos existentes). - Volume da cauda abaixo de -80 dB: a gravação provavelmente foi interrompida no meio; confirme com o usuário, não trate como um problema de edição.
- Nenhum trecho de regravação encontrado: verifique se a transcrição foi feita a partir do
voice_master.wave veja como a palavra-chave foi escrita na transcrição; adicione essa grafia com--phrase. - Ver cada comando executado: use
bash -x <caminho_desta_skill>/scripts/edit_podcast.sh .... - Ao escrever parâmetros de filtro do ffmpeg no shell zsh: quando uma variável é seguida imediatamente por dois pontos, escreva
${VAR}:...; caso contrário, o zsh interpretará$VAR:ae$VAR:rcomo modificadores, alterando silenciosamente os parâmetros.
Licença: código MIT, documentação CC BY 4.0.