joshua.seigler.net/posts/ffmpeg-audio-cleanup/index.html
Joshua Seigler 1fda54786a Updates
2025-07-09 18:40:00 -04:00

23 lines
No EOL
8.2 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"><link rel="preconnect" href="https://stats.apps.seigler.net/"><style>@font-face{font-family:'Inter Variable';font-style:normal;font-display:swap;font-weight:100 900;src:url(/fonts/inter-latin-wght-normal.woff2) format('woff2-variations');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Inter Variable';font-style:italic;font-display:swap;font-weight:100 900;src:url(/fonts/inter-latin-wght-italic.woff2) format('woff2-variations');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}</style><script defer src="https://stats.apps.seigler.net/script.js" data-website-id="ccb4bd94-2a71-47fe-8eea-d85bf75b7f6d"></script><link rel="me" href="https://github.com/seigler"><link rel="webmention" href="https://webmention.io/joshua.seigler.net/webmention"><title>FFmpeg audio cleanup - joshua.seigler.net</title><meta name="description" content="A script to apply dynamic compression and noise reduction to audio files"><meta name="keywords" content="posts, technical, ffmpeg"><meta property="og:title" content="FFmpeg audio cleanup"><meta property="og:type" content=""><meta property="og:url" content="/posts/ffmpeg-audio-cleanup/"><meta name="twitter:title" content="FFmpeg audio cleanup"><meta name="twitter:description" content="A script to apply dynamic compression and noise reduction to audio files"><meta name="twitter:card" content="summary"><meta name="generator" content="Eleventy v3.1.0"><script type="module" crossorigin src="/assets/main-BcCmWSCK.js"></script><link rel="stylesheet" crossorigin href="/assets/main-MPbmXHLu.css"></head><body data-font="english" data-path="/posts/ffmpeg-audio-cleanup/"><header><nav><div class="nav-row"><div class="nav-home"><a href="/">joshua.seigler.net</a></div></div><div class="nav-categories"><a aria-current="page" href="/posts/">/posts</a> <a href="/about/">/about</a> <a href="/now/">/now</a> <a href="/uses/">/uses</a> <a href="/recipes/">/recipes</a> <a href="/music/">/music</a> <a href="/books/">/books</a> <a href="/links/">/links</a> <a href="/search/">/search</a></div></nav><h1>FFmpeg audio cleanup</h1><div class="header-meta"><span>June 26, 2025</span> <span class="tags"><a class="tag" style="--tagIndex:5" href="/tags/technical/">technical</a> <a class="tag" style="--tagIndex:10" href="/tags/ffmpeg/">ffmpeg</a></span></div></header><header class="toc"></header><main data-pagefind-body="data-pagefind-body"><p>I recently needed to process 20+ phone audio recordings. The files are mp3 recordings in stereo, made in an environment with echoes and noise from fans/heaters.</p><p>Although I could do it easily with <a href="https://tenacityaudio.org/" target="_blank" rel="noopener">Tenacity</a> I didnt want to use a manual process, since it would take days. So I tried using FFmpeg filters and Bash scripting.</p><p>I found an FFmpeg filter called <a href="https://ffmpeg.org/ffmpeg-filters.html#compand" target="_blank" rel="noopener">compand</a> which lets you map an input decibel range to an output decibel range. I also used the <a href="https://ffmpeg.org/ffmpeg-filters.html#anlmdn" target="_blank" rel="noopener">anlmdn</a> filter to reduce noise, and the <a href="https://ffmpeg.org/ffmpeg-filters.html#highpass" target="_blank" rel="noopener">highpass</a> filter to help with clarity.</p><p>I ran into a couple gotchas.</p><ol><li><code>mpv</code> does something special for audio playback that prevents audio from clipping. <code>vlc</code> plays the file as it is.</li><li>Because the compressor has an attack and decay (which is necessary for things to sound good) it can cause clipping if the volume rises sharply. Applying a <code>delay</code> parameter with half the duration of the attack length fixed this.</li></ol><p>Here is the script:</p><p><code>process-audio.sh</code></p><pre class="language-bash"><code class="language-bash"><span class="token shebang important">#!/bin/bash</span>
<span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token string">"<span class="token variable">$#</span>"</span> <span class="token operator">==</span> <span class="token string">"0"</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span>
<span class="token builtin class-name">echo</span> <span class="token string">"Error: no arguments provided."</span>
<span class="token builtin class-name">echo</span> <span class="token string">"Usage: <span class="token variable">$0</span> file1 file2 file3 ..."</span>
<span class="token builtin class-name">echo</span> <span class="token string">"or <span class="token variable">$0</span> *.ext"</span>
<span class="token builtin class-name">exit</span> <span class="token number">1</span>
<span class="token keyword">fi</span>
<span class="token builtin class-name">trap</span> <span class="token string">"exit"</span> INT
<span class="token keyword">while</span> <span class="token punctuation">[</span> <span class="token string">"<span class="token variable">$#</span>"</span> <span class="token operator">!=</span> <span class="token string">"0"</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">do</span>
<span class="token assign-left variable">path</span><span class="token operator">=</span><span class="token string">"<span class="token variable">${1<span class="token operator">%</span><span class="token operator">/</span>*}</span>"</span>
<span class="token assign-left variable">file</span><span class="token operator">=</span><span class="token string">"<span class="token variable">${1<span class="token operator">##</span>*<span class="token operator">/</span>}</span>"</span>
<span class="token assign-left variable">outfile</span><span class="token operator">=</span><span class="token string">"./normalized--<span class="token variable">$file</span>"</span>
<span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token operator">!</span> <span class="token parameter variable">-f</span> <span class="token string">"<span class="token variable">$outfile</span>"</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span>
<span class="token builtin class-name">echo</span> <span class="token string">"Processing <span class="token variable">$1</span>"</span>
ffmpeg <span class="token parameter variable">-i</span> <span class="token string">"<span class="token variable">$1</span>"</span> <span class="token parameter variable">-v</span> warning <span class="token parameter variable">-ac</span> <span class="token number">1</span> <span class="token parameter variable">-af</span> <span class="token string">"compand=attacks=0.3:decays=0.3:delay=0.15:points=-80/-300|-45/-25|-27/-15|0/-12|20/-12,anlmdn=s=10,highpass=f=500"</span> <span class="token parameter variable">-threads</span> <span class="token number">4</span> <span class="token string">"<span class="token variable">$outfile</span>"</span>
<span class="token keyword">else</span>
<span class="token builtin class-name">echo</span> <span class="token string">"Skipping <span class="token variable">$1</span>, already processed."</span>
<span class="token keyword">fi</span>
<span class="token builtin class-name">shift</span>
<span class="token keyword">done</span>
</code></pre><p>If this is useful to you please leave a comment or send an email, I would love to hear about it.</p></main><script data-isso="//comments.apps.seigler.net/" src="//comments.apps.seigler.net/js/embed.min.js"></script><section id="isso-thread" data-title=""><noscript>Javascript needs to be activated to view comments.</noscript></section><footer>&copy; Joshua Seigler 2025. - <a rel="me" href="mailto:joshua@seigler.net?subject=Hello">Contact</a> - <a href="/feed.xml">RSS</a> - <a href="/unoffice-hours/">Unoffice Hours</a> - <a href="/webrings/">Webrings</a></footer><div id="effects"></div></body></html>