Edición y masterización de podcasts
Convierte las grabaciones de podcasts o locuciones del usuario en episodios listos para publicar: reduce ruido, recorta silencios de inicio y final, ecualiza y comprime la voz, normaliza la sonoridad a aproximadamente -16 LUFS y exporta a MP3. Cuando sea necesario, añade música de introducción, cierre y segmentada, y ofrece sugerencias de edición (qué pausas largas se pueden acortar, dónde hay segmentos regrabados) que el usuario confirma antes de cortar.
Los directorios scripts/, references/ están dentro del directorio de esta habilidad. Al ejecutar, escribe las rutas completas, por ejemplo: bash <directorio_de_la_habilidad>/scripts/edit_podcast.sh.
1. Antes de empezar, explícale al usuario
- Debe tener
ffmpeginstalado (herramienta de audio gratuita y de código abierto). Si no lo tiene, pídele que lo instale: en macOS conbrew install ffmpeg, en Linux con el gestor de paquetes (p. ej.,apt install ffmpeg). El script busca en este orden: variable de entornoFFMPEG→ffmpegen el PATH del sistema → ubicaciones comunes/opt/homebrew/bin,/usr/local/bin. Probado con las versiones 6.0 y 8.1. - Requiere bash y Python 3.9 o superior, solo biblioteca estándar, no es necesario instalar paquetes adicionales de Python.
- Solo compatible con macOS y Linux: probado en macOS; debería funcionar en Linux; Windows requiere WSL, pero no está probado.
- La primera reducción de ruido descargará un modelo público de aproximadamente 300 KB (RNNoise) y lo guardará en
~/.cache/ethan-podcast-post/. Después de la descarga, se verifica con SHA-256. Si no se puede descargar o la verificación falla, el script usará automáticamente el filtroafftdnincorporado en ffmpeg. El script indicará qué método usa; debes informar al usuario. El origen del modelo se encuentra enreferences/third-party.md. - Todo el proceso se realiza en la máquina local: no se sube la grabación, no hay costos.
- Lo que no hacemos: no generamos ni clonamos voces, no doblamos, no transcribimos (si se necesitan subtítulos, el usuario debe proporcionar un archivo SRT), no publicamos en ninguna plataforma, no proporcionamos música (la música de fondo la proporciona el usuario y debe confirmar que tiene los derechos de uso).
2. Seguridad de los archivos (obligatorio)
- La grabación original es de solo lectura: el script solo la lee, no la modifica, no la mueve, no la elimina.
- Todo el resultado se escribe en un directorio nuevo: se crea un directorio junto a la grabación (p. ej.,
Episodio3_edicion/), sin mezclarse con la grabación o la música de fondo. El script se detiene si descubre que el directorio de salida está en el mismo directorio que el archivo de entrada. - No sobrescribir sin permiso: si en el directorio de salida ya existen archivos como
episodio_final.mp3,audio_raw.wav,voice_master.wav, el script se detiene para no sobrescribirlos. Para sobrescribir, primero dile al usuario qué archivos se sobrescribirán y que los archivos antiguos no se pueden recuperar; solo después de que el usuario acepte, ejecuta de nuevo conOVERWRITE=1. Lo mismo aplica para las sugerencias de edición, el corte y los scripts de procesamiento de voz: se detienen si ya existe un archivo de salida con el mismo nombre; el usuario debe aceptar antes de usar--overwrite. - Confirmar antes de eliminar: cualquier eliminación (incluyendo la limpieza de archivos grandes como
audio_raw.wav) debe comunicarse al usuario (qué archivo y por qué) y obtener su consentimiento antes de eliminar. Los archivos intermedios del script se colocan en un subdirectorio temporal específico de la ejecución dentro del directorio de salida; al final, solo se limpia ese subdirectorio. - Lo que se corta lo decide el usuario: las sugerencias de edición son solo una lista de candidatos; no se corta nada hasta que el usuario lo confirme.
3. Flujo de procesamiento
Primero, ejecuta una comprobación del entorno e informa al usuario:
bash <directorio_de_la_habilidad>/scripts/edit_podcast.sh --check
Esto mostrará la ubicación de ffmpeg, su versión, si la ecualización dinámica de 250 Hz está habilitada (ver sección 6) y si el modelo de reducción de ruido está listo. Si no encuentra ffmpeg, indicará cómo instalarlo; primero hay que instalarlo y luego continuar.
Paso 1: Primer procesamiento, genera la master de voz y la versión de prueba
KEEP_MASTER=1 bash <directorio_de_la_habilidad>/scripts/edit_podcast.sh <archivo_de_grabación> <directorio_de_salida>/pass1 <nombre>
- La grabación puede ser wav, m4a, mp3, mp4 o cualquier formato que
ffmpegpueda leer. Para conversaciones de dos o más personas, añadeINTERVIEW_MODE=1(compresión más fuerte y nivelación automática del volumen entre personas). - Orden de procesamiento: extraer a 48 kHz WAV → control de calidad → recortar silencios de inicio y final (las pausas intermedias no se tocan) → eliminar chasquidos de labios y dientes → filtro paso alto de 80 Hz → reducción de ruido → ecualización dinámica de 250 Hz → desesibilización → compresión → realce de 5 kHz → ganancia lineal a -16 LUFS → limitador → exportar a MP3 → remedición de sonoridad.
- Resultados:
<nombre>_final.mp3: episodio sin música (también sirve como versión de prueba);voice_master.wav: master de voz (ya recortada al inicio y final); las transcripciones, sugerencias de edición y cortes posteriores se basarán en este archivo;audio_raw.wav: copia WAV de la grabación original (se escribe en el directorio de salida solo después de que todo el procesamiento haya tenido éxito); puede ser útil para rehacer, pero ocupa espacio.
- Revisa estos elementos de la salida e informa al usuario:
- Volumen al final por debajo de -80 dB: la grabación podría haberse interrumpido; primero confírmalo con el usuario;
- Método de reducción de ruido: RNNoise o afftdn;
- La sonoridad del episodio final debe estar entre -16 LUFS ±1, y el pico verdadero no debe superar -1.5 dBTP. Si se excede, el script lo indicará; díselo al usuario tal cual.
- Si la grabación es demasiado baja o está vacía, el script generará un error y se detendrá, sin producir un episodio vacío. En el error se dará un valor sugerido basado en el pico de la grabación: primero confirma con el usuario que la grabación contiene voz; luego, ejecuta de nuevo añadiendo
INPUT_GAIN=<valor_sugerido>(en dB, ya incluye elINPUT_GAINactual) para elevar todo el segmento a un nivel normal; cuando se produce el error, no quedan archivos en el directorio de salida, por lo que se puede volver a ejecutar usando el mismo directorio. Al mismo tiempo, sugiere al usuario que en el futuro aumente el volumen de entrada al grabar.
Paso 2: Sugerencias de edición (solo lista de candidatos, no se modifica el audio)
La transcripción debe hacerse a partir del voice_master.wav del paso 1 (o del MP3 del episodio sin música del paso 1, ya que ambos tienen la misma línea de tiempo). No uses la transcripción de la grabación original: la original no tiene los silencios de inicio y final recortados, por lo que los tiempos no coincidirán y las ubicaciones encontradas serán incorrectas.
Para encontrar pausas largas que se puedan acortar:
python3 <directorio_de_la_habilidad>/scripts/edit_cuts.py suggest <directorio_de_salida>/pass1/voice_master.wav \
[--srt subtitulos.srt] [--protect 15:00.0-15:03.0 ...]
- Por defecto, considera como candidatas las pausas de más de 1.5 segundos y sugiere acortarlas a 1.2 segundos (dejando la mitad de la pausa a cada lado para mantener el ruido ambiental de la sala).
- Las pausas que el usuario haya dejado intencionadamente (por ejemplo, silencio después de una frase importante) se marcan con
--protect inicio-fin, dejando 0.5 segundos de margen a cada lado; no se sugerirá cortarlas. - Si no se encuentran pausas largas evidentes, es posible que el ruido de fondo sea alto; ajusta
--noisede -45 (valor por defecto) a -40 y vuelve a ejecutar. - Si hay más de 15 candidatos o más de 60 segundos acumulados por cada 40 minutos de audio, el script advertirá que los parámetros son demasiado estrictos: aumenta
--min-pausey no cortes todo sin criterio.
Para encontrar segmentos regrabados marcados con una palabra clave (necesita transcripción):
python3 <directorio_de_la_habilidad>/scripts/edit_cuts.py retakes <directorio_de_salida>/pass1/voice_master.wav --srt subtitulos.srt [--phrase tu_palabra_clave]
- Convención al grabar: si te equivocas → pausa de más de 1 segundo → di la palabra clave con calma → pausa de más de 1 segundo → vuelve a decir la frase desde el principio.
- La palabra clave por defecto es «repite repite». Para usar tu propia palabra clave, usa
--phrase; si el software de transcripción suele escribir la palabra clave con homófonos, puedes usar varias veces--phrasepara incluir todas las variantes. Elige una frase que no aparezca en el texto normal; decirla dos veces seguidas reduce las falsas detecciones. - El script busca la palabra clave en la transcripción, usa el inicio de la frase repetida para encontrar la frase incorrecta anterior y proporciona el intervalo de corte y un nivel de confianza (high / mid / low). Los de nivel «low» deben ser escuchados por el usuario para confirmar.
Ambos comandos generan voice_master_suggested_cuts.tsv y voice_master_retake_cuts.tsv respectivamente (si ya existen, no se sobrescriben; el usuario debe aceptar con --overwrite). Explica cada candidato al usuario (tiempo, contexto, cuántos segundos se ahorran). Después de que el usuario confirme qué cortes quiere, copia esas líneas en un nuevo archivo cuts.tsv (separado por tabuladores: inicio, fin, nota; el tiempo en formato MM:SS.s).
Paso 3: Corte
python3 <directorio_de_la_habilidad>/scripts/edit_cuts.py apply <directorio_de_salida>/pass1/voice_master.wav --cuts cuts.tsv [--srt subtitulos.srt]
- Corta por muestra, con un fundido cruzado de potencia constante de 15 milisegundos en las uniones. Genera un archivo
voice_master_edited.wavde 24 bits y un mapa de ediciónvoice_master_edited.edit_map.json(que registra el origen y la posición de cada segmento conservado). - Si se proporciona
--srt, los subtítulos completos se desplazan automáticamente; si algún subtítulo se corta por la mitad, el script se negará a ejecutarlo: elimina--srty vuelve a ejecutar; después del corte, pide al usuario que vuelva a transcribir la master editada. - Después del corte, pide al usuario que escuche unos segundos alrededor de cada unión: sin chasquidos, sin dos inspiraciones, sin cambios bruscos de timbre.
Paso 4 (opcional): Cambiar la textura de la voz para citas, recuerdos, etc.
python3 <directorio_de_la_habilidad>/scripts/voice_fx.py <master_editada.wav> --fx fx.tsv
python3 <directorio_de_la_habilidad>/scripts/voice_fx.py --list # Ver los cinco ajustes preestablecidos
- Ajustes preestablecidos: quote (citar cartas, textos de otros), archive (periódicos antiguos, documentos antiguos), telephone (relatar una conversación telefónica), memory (recuerdos, sueños), radio (radio antigua, máximo una vez por episodio).
- fx.tsv cada línea: ajuste, inicio, fin, fundido de entrada, fundido de salida, nota (separados por tabulador; escribe «-» para usar el valor por defecto).
- Después del procesamiento, la duración no cambia (por muestra) y la sonoridad no cambia; el resultado es
<nombre_original>_fx.wav.
Paso 5 (opcional): Música y tabla de cues
Solo se hace si el usuario proporciona música de fondo. Primero escribe la tabla de cues, luego haz una validación previa. La tabla de cues se coloca en el directorio de salida del segundo pase; el nombre del archivo debe ser <nombre>_cues.tsv.
Tabla de cues: 7 columnas separadas por tabulador. Las líneas que comienzan con # se ignoran. Escribe - en una columna para usar el valor por defecto. time se escribe en MM:SS.s, relativo a la línea de tiempo de la master de voz. Los siguientes son ejemplos:
# 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 | Función | dur | Volumen por defecto |
|---|---|---|---|
| stinger | Un breve fragmento musical, superpuesto a la voz | Por defecto 6 segundos | 0.12 |
| bed / post | Música de fondo que se atenúa automáticamente cuando aparece la voz | Obligatorio | 0.12 |
| break | En ese punto, corta la voz, inserta un fragmento de música pura, y al terminar vuelve a la voz; el episodio se alarga dur segundos | Obligatorio | 0.5 |
| ending | Música de cierre: se atenúa mientras se habla, sube al terminar y finalmente se desvanece | Escribe - | 0.22 |
srcescribe-para usar la música de fondo proporcionada en esta ejecución;-@20indica empezar desde el segundo 20 de la música de fondo; también se puede escribir la ruta completa de otro archivo de música, igualmente con@segundos(sin@se empieza desde el principio).- Para stinger, si
srces-sin@, por defecto toma un fragmento desde el segundo 60 de la música de fondo; para otros tipos, sin@se empieza desde el principio. - Después de escribir la tabla, haz una validación previa; si hay ❌, corrígelo primero:
python3 <directorio_de_la_habilidad>/scripts/cues_lint.py <nombre>_cues.tsv --master <master_editada.wav> --bg <música_de_fondo>
Esto verificará el formato, límites, existencia de materiales, superposición de ventanas y proporción total de música (se recomienda no superar el 15%). También proporciona una tabla de conversión de tiempos «master → episodio» para usarla directamente al escribir la línea de tiempo del programa.
Paso 6: Segundo pase para generar el episodio final
MASTER_WAV=<master_editada.wav> bash <directorio_de_la_habilidad>/scripts/edit_podcast.sh - [música_de_fondo] <directorio_de_salida>/final <nombre>
- El primer parámetro es
-. Cuando existeMASTER_WAV, se salta la extracción, reducción de ruido y normalización, y directamente superpone los cues, mezcla la música y exporta. - Si hay música de fondo y el audio dura más de 80 segundos, se añade automáticamente música de introducción y cierre: la música de introducción suena 3 segundos antes de que entre la voz; después de que termine la voz, la música se desvanece durante unos 7 segundos; cuando aparece la voz, la música se atenúa automáticamente.
- Si no hay cortes ni música, el episodio del paso 1 es el episodio final; no es necesario ejecutar este paso.
Paso 7: Verificación
El script ya realiza una remedición al final. Para medir de forma independiente (usa la ruta de ffmpeg que se muestra con --check):
"<ruta_de_ffmpeg>" -hide_banner -nostats -i <episodio_final.mp3> -filter_complex ebur128=peak=true -f null - 2>&1 | tail -12
- Criterios de aprobación: Integrated entre -16 LUFS ±1, True peak no superior a -1.5 dBFS (es decir, dBTP).
- Pide al usuario que use auriculares y verifique: la música no tapa la voz; en las pausas, la música no vuelve bruscamente; cada punto de corte es natural; el final no se corta abruptamente; las pausas intencionadas siguen ahí.
- Al entregar, informa al usuario: ubicación del episodio final, método de reducción de ruido, valores medidos de sonoridad y pico verdadero, y qué cortes se hicieron. Si no cumple los criterios, dilo tal cual, no digas que pasa.
4. Principios de edición
- Solo corta accidentes, no respiraciones: errores al hablar, distracciones al leer el guion, segmentos regrabados inútiles se cortan; las respiraciones, los «eh» de pensamiento, las dudas entre frases y los silencios intencionados se dejan. Si no estás seguro, déjalo.
- Las pausas de menos de 0.8 segundos no se tocan; las pausas entre frases de 0.8–1.5 segundos se dejan todas por defecto; las pausas más largas solo se acortan si hay evidencia de tartamudeo o distracción.
- Los cortes se hacen en puntos de respiración naturales, sin dañar el inicio de una palabra ni el final de un tono; las respiraciones se dejan enteras o se eliminan por completo.
- La música de fondo debe estar por debajo de la voz, evitando canciones con voz y piezas con mucho contenido en frecuencias medias (1–3 kHz); la música melódica se coloca preferiblemente entre párrafos (breaks), no superpuesta a la voz; la duración total de la música no debe superar el 15% del episodio.
- Para más detalles, consulta
references/editing-style-guide.md(estilo y presentación),references/editing-essentials.md(puntos clave de ejecución y verificación) yreferences/audio-glossary.md(glosario de términos de audio).
5. Variables de entorno (todas opcionales)
| Variable | Valor por defecto | Función |
|---|---|---|
FFMPEG | Búsqueda automática | Especifica la ruta de ffmpeg |
DENOISE | rnnoise | rnnoise / afftdn / off |
RNNOISE_MODEL | ~/.cache/ethan-podcast-post/bd.rnnn | Ubicación del modelo de reducción de ruido; si ya tienes el modelo, puedes apuntar directamente a él |
RNNOISE_URL | Modelo público en GitHub | Si la red no funciona, puedes cambiarlo por una URL espejo (la descarga aún se verifica con SHA-256) |
INPUT_GAIN | 0 | Aumenta (o reduce) el volumen general de la grabación antes del procesamiento en dB; si la grabación es demasiado baja, usa el valor sugerido en el error |
TRIM_THRESHOLD | -50 | Umbral para recortar silencios de inicio y final (dB); si se corta un susurro al inicio o final, bájalo, p. ej., a -60 |
INTERVIEW_MODE | 0 | 1 = conversación de dos o más personas |
KEEP_MASTER | 0 | 1 = conservar la master de voz voice_master.wav |
MASTER_WAV | Vacío | Master de voz para el segundo pase |
MUSIC_VOLUME | 0.12 | Proporción de volumen de la música de fondo |
INTRO_DURATION | 40 | Ventana en segundos para la música de introducción y cierre; si el audio es más corto que el doble de este valor, no se añade |
PREROLL / TAIL | 3 / 7 | Segundos de música de introducción antes de la voz / segundos de música después de que termine la voz |
OUTRO_SEEK | Vacío | Desde qué segundo de la canción se toma la música de cierre |
MP3_BITRATE | 160k | Tasa de bits del episodio final |
OVERWRITE | 0 | 1 = permitir sobrescribir archivos finales existentes (primero obtener el consentimiento del usuario) |
6. Preguntas frecuentes
- No encuentra
ffmpeg: pide al usuario que lo instale (macOSbrew install ffmpeg, Linux con el gestor de paquetes), o especifica la ruta conFFMPEG=/ruta_completa/ffmpeg. - Versión de
ffmpeg: el script lee el número de versión. El significado de los parámetros en el nivel de ecualización dinámica de 250 Hz cambió entre ffmpeg 6 y 8; ambas versiones se han calibrado con señales sinusoidales y el efecto es consistente; con versiones no verificadas como 7.x, se omitirá este nivel y se mostrará un aviso; el resto del procesamiento continúa con normalidad. Los parámetros para recortar silencios de inicio y final también se han ajustado para el comportamiento de las versiones nuevas; 6.x y 8.x cortan de la misma manera. - Error «Después de recortar silencios de inicio y final, no queda casi nada» o «No se puede medir la sonoridad después del procesamiento»: la grabación es demasiado baja o está vacía. Después de confirmar que hay voz, ejecuta de nuevo con el valor sugerido de
INPUT_GAINdel error (si la sonoridad está por debajo de -70 LUFS, no se puede medir; solo ajustar el umbral de recorte no es suficiente); si realmente no hay sonido en la grabación, el error se repetirá y no se generará un episodio vacío. - La reducción de ruido usó
afftdn: significa que el modelo no se descargó correctamente. Puedes configurarRNNOISE_URLpara usar un espejo, o usarRNNOISE_MODELpara apuntar a un archivo de modelo existente, y luego volver a ejecutar (pregunta al usuario antes de sobrescribir archivos finales existentes). - Volumen al final por debajo de -80 dB: la grabación probablemente se interrumpió; primero confírmalo con el usuario, no lo trates como un problema de edición.
- No se encontró ningún segmento regrabado: confirma que la transcripción se hizo a partir de
voice_master.wav, y luego revisa cómo se escribió la palabra clave en la transcripción; añade esa variante con--phrase. - Quiero ver el comando de cada paso:
bash -x <directorio_de_la_habilidad>/scripts/edit_podcast.sh .... - Al escribir parámetros de filtros de
ffmpegen zsh: cuando una variable va seguida inmediatamente de dos puntos, escríbela como${VAR}:...; de lo contrario, zsh interpretará$VAR:a,$VAR:rcomo modificadores y los parámetros se modificarán silenciosamente.
Licencia: código MIT, documentación CC BY 4.0.