Skip to content

Instantly share code, notes, and snippets.

@milnak
Last active August 15, 2024 18:08
Show Gist options
  • Save milnak/8f7c24514b2d5bb430092ba15c6c2d99 to your computer and use it in GitHub Desktop.
Save milnak/8f7c24514b2d5bb430092ba15c6c2d99 to your computer and use it in GitHub Desktop.
Script to normalize MP3 and FLAC: Uses ffmpeg-normalize and ffmpeg
# Normalize mp3 and flac files in current directory using ffmpeg-normalize
# scoop install ffmpeg
$ffmpeg = 'ffmpeg.exe'
# scoop install python
# pip3.exe install ffmpeg-normalize
$ffmpeg_normalize = 'ffmpeg-normalize.exe'
function Get-MeanVolume {
Param([Parameter(Mandatory = $true)][string]$File)
$psi = New-Object Diagnostics.ProcessStartInfo
$psi.FileName = $ffmpeg
$psi.RedirectStandardError = $true
$psi.RedirectStandardOutput = $false
$psi.UseShellExecute = $false
$psi.Arguments = "-hide_banner -nostdin -i `"{0}`" -filter:a volumedetect -f null /dev/null" -f $File
$proc = New-Object System.Diagnostics.Process
$proc.StartInfo = $psi
$proc.Start() | Out-Null
# Important: ReadToEnd must come before WaitForExit!
$err = $proc.StandardError.ReadToEnd()
$proc.WaitForExit() | Out-Null
[double]($err -split "`r`n" | Select-String 'mean_volume: (.+) dB').Matches.Groups[1].Value
}
function Normalize-File {
Param(
[Parameter(Mandatory = $true)][string]$File,
[int]$TargetLevel = -18,
[string]$OutputFolder = 'NORMALIZED'
)
# Extension without dot.
$ext = [IO.Path]::GetExtension($File).Substring(1)
# Why -c:a ext ?
# See https://github.com/slhck/ffmpeg-normalize#could-not-write-header-for-output-file-error
# The conversion does not work and I get a cryptic ffmpeg error!
& $ffmpeg_normalize --output-folder $OutputFolder --normalization-type rms --target-level $TargetLevel --force -c:a $ext --extension $ext $File
}
if (-not (Get-Command $ffmpeg -CommandType Application -ErrorAction SilentlyContinue)) {
"'{0}' not found." -f $ffmpeg
return
}
if (-not (Get-Command $ffmpeg_normalize -CommandType Application -ErrorAction SilentlyContinue)) {
"'{0}' not found." -f $ffmpeg_normalize
return
}
Get-ChildItem -File | Where-Object Extension -in '.mp3', '.flac' | ForEach-Object {
# --target-level -13 (Average RMS Power in dbFS) is what pynormalize uses https://pypi.org/project/pynormalize/
# but that seems too aggressive, so I'm using -18.
[int]$targetLevel = -18
$_.Name
[double]$level = Get-MeanVolume -File $_.FullName
if ([int]$level -ge [int]$targetLevel) {
"`tLevel $level is fine. Skipping"
}
else {
Normalize-File -File $_.FullName -TargetLevel $targetLevel -OutputFolder 'NORMALIZED'
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment