Skip to content

Instantly share code, notes, and snippets.

@jult
Last active April 18, 2026 07:15
Show Gist options
  • Select an option

  • Save jult/27230b590fedcd737ce8945277a17e82 to your computer and use it in GitHub Desktop.

Select an option

Save jult/27230b590fedcd737ce8945277a17e82 to your computer and use it in GitHub Desktop.
powershell 7+ script to normalize & downsample FLAC 192kHz to 96 kHz (for stems compatibility of denon equipment)
<#
.SYNOPSIS
Resample FLAC 192kHz→96kHz + Peak Normalize to 0dBFS.
Uses FFmpeg + soxr, max FLAC compression, metadata preservation.
PowerShell 7+ required.
.NOTES
- Two-pass process per file: volumedetect → resample+normalize
- Parallel execution maxing out CPU/RAM
- All tags, cover art, and metadata preserved
#>
# ==================== USER CONFIGURATION ====================
# ⚠️ UPDATE THESE PATHS TO MATCH YOUR SYSTEM ⚠️
$FFmpegPath = "P:\Portable\pz\tools\FFmpeg64\ffmpeg.exe" # Your FFmpeg path
$MetaflacPath = "P:\Portable\flac\tools\metaflac.exe" # Your metaflac path
$SourceDir = "J:\audio\!OUT\stemsfail\192kc" # Folder with 192kHz FLACs
$DestDir = "J:\xff\@utput" # Output folder
$Threads = 26 # Parallel jobs (logical cores)
# Soxr resample filter (audiophile-grade)
$ResampleFilter = "aresample=96000:resampler=soxr:precision=33:cheby=1"
# ============================================================
# Validate executables
@{ FFmpeg = $FFmpegPath; metaflac = $MetaflacPath }.GetEnumerator() | ForEach-Object {
if (!(Test-Path -LiteralPath $_.Value)) {
throw "$($_.Key) not found at: $($_.Value)`nPlease update the paths at the top of this script."
}
}
# Create output directory
if (!(Test-Path -LiteralPath $DestDir)) {
New-Item -ItemType Directory -LiteralPath $DestDir | Out-Null
}
Write-Host "`n========================================"
Write-Host " FLAC 192kHz → 96kHz + Peak Normalize (0 dBFS)"
Write-Host " Quality: soxr precision=33, cheby=1"
Write-Host " Compression: FLAC level 8 (smallest lossless)"
Write-Host " Threads: $Threads"
Write-Host "========================================`n"
# Process files in parallel
Get-ChildItem -LiteralPath $SourceDir -Filter "*.flac" -File | ForEach-Object -Parallel {
$File = $_
$TempFile = Join-Path $env:TEMP "FFmpeg_$($_.BaseName).flac"
$TagsFile = Join-Path $env:TEMP "FFmpeg_$($_.BaseName).tags"
$OutputFile = Join-Path $using:DestDir $File.Name
$ffmpeg = $using:FFmpegPath
$metaflac = $using:MetaflacPath
$resFilter = $using:ResampleFilter
try {
# 1️⃣ Export Vorbis comments/tags
& $metaflac "--export-tags-to=$TagsFile" $File.FullName 2>$null
# 2️⃣ PASS 1: Analyze peak level with volumedetect
$analyzeLog = & $ffmpeg -nostdin -i $File.FullName -af "volumedetect" -f null -y - 2>&1
$peakMatch = [regex]::Match(($analyzeLog -join "`n"), 'max_volume:\s*(-?\d+(?:\.\d+)?)\s*dB')
$gain = 0.0
if ($peakMatch.Success) {
$peak = [double]$peakMatch.Groups[1].Value
if ($peak -lt 0) { $gain = -$peak } # Boost quiet files to 0 dBFS
}
# 3️⃣ PASS 2: Resample + Normalize + Encode in one go
$afChain = if ($gain -gt 0) { "$resFilter,volume=${gain}dB" } else { $resFilter }
$result = & $ffmpeg -nostdin -y -i $File.FullName `
-af $afChain `
-c:a flac `
-compression_level 8 `
-map 0 `
-map_metadata 0 `
$TempFile `
2>&1
if ($LASTEXITCODE -ne 0) {
throw "FFmpeg encode failed: $($result -join "`n")"
}
# 4️⃣ Verify output sample rate
$probeOutput = & $ffmpeg -nostdin -i $TempFile -hide_banner 2>&1
$rateMatch = [regex]::Match(($probeOutput -join "`n"), '(\d+)\s*Hz')
$actualRate = $rateMatch.Groups[1].Value
if ([string]::IsNullOrWhiteSpace($actualRate) -or $actualRate -ne "96000") {
throw "Verification failed: output sample rate is $actualRate Hz (expected 96000)"
}
# 5️⃣ Restore metadata tags
if (Test-Path -LiteralPath $TagsFile) {
& $metaflac "--import-tags-from=$TagsFile" $TempFile 2>$null
Remove-Item -LiteralPath $TagsFile -Force -ErrorAction SilentlyContinue
}
# 6️⃣ Move verified file to destination
Move-Item -Force -LiteralPath $TempFile -Destination $OutputFile
$gainLabel = if ($gain -gt 0) { "[+${gain}dB]" } else { "[0dB]" }
Write-Host "[✅ SUCCESS] $gainLabel $($File.Name)"
} catch {
Remove-Item -LiteralPath $TempFile, $TagsFile -Force -ErrorAction SilentlyContinue
Write-Host "[❌ FAILED] $($File.Name) | $($_.Exception.Message)"
}
} -ThrottleLimit $Threads
Write-Host "`n========================================"
Write-Host " All done! Output saved to: $DestDir"
Write-Host "========================================"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment