Batch podcast cleanup
Clean and level a whole folder of episodes in one call. Every command takes several inputs or a glob, processes each file the same way, and returns one envelope with a result per file. Run with ferraus 0.1.0; JSON is trimmed.
episodes/ep01.wav episodes/ep02.wav episodes/ep03.flac
1. Measure them all
ferraus analyze loudness "episodes/*.wav" "episodes/*.flac" --json
{"ok": true, "command": "analyze loudness",
"inputs": [{"path": "episodes/ep01.wav", ...}, {"path": "episodes/ep02.wav", ...}, {"path": "episodes/ep03.flac", ...}],
"result": [
{"input": "episodes/ep01.wav", "metrics": {"integrated_lufs": -16.4262, "loudness_range_lu": 1.7741, "true_peak_dbtp": -1.8811, ...}, "result": {...}},
{"input": "episodes/ep02.wav", "metrics": {"integrated_lufs": -22.4262, ...}, "result": {...}},
{"input": "episodes/ep03.flac", ...}]}
Quote globs so ferraus expands them (the same on every shell, including PowerShell). Name
the extensions: a bare "episodes/*" also matches the .ferraus.json sidecars that ferraus
writes beside its outputs. Each of those becomes a failed item with unsupported_format, and
because one item failed, the envelope's top-level ok is false and the exit code is 4, even
though every audio file succeeded. In a batch, result[] has one entry per input; failed
entries carry "ok": false and an error object, so check each entry.
2. Clean, level, and normalize in one pass per file
ferraus fx "episodes/*.wav" "episodes/*.flac" --fx dehum --fx "denoise:auto" --fx leveler \
--fx "enhance-voice:mode=female" --fx "normalize:lufs=-16" --outdir clean --json
{"ok": true, "command": "fx",
"outputs": [{"path": "clean/ep01.fx.wav"}, {"path": "clean/ep02.fx.wav"}, {"path": "clean/ep03.fx.flac"}],
"result": [{"input": "episodes/ep01.wav", "metrics": {"freq_hz": 60.001, "hum_reduction_db": 16.7, "noise_floor_db": -81.2, ...}}, ...]}
--outdir cleanis required for a batch unless-ois a template. Outputs keep the input name plus the command (ep01.fx.wav) and the input's format.- Use an
-otemplate for other names:-o 'clean/{name}.wav'({name}is the input file's stem,{index}its position in the batch,{ext}its extension). denoise:autolearns the noise of each file separately, andparams.noise_regionper item says where. Check that each landed on a pause.
If you only need the loudness step (the "normalize every episode to -16 LUFS" job):
ferraus normalize "episodes/*.wav" --lufs -16 --outdir loud --json
--dry-run shows the output names first:
{"ok": true, "command": "normalize", "dry_run": true,
"outputs": [{"path": "loud/ep01.normalize.wav"}, {"path": "loud/ep02.normalize.wav"}],
"params": {"lufs": "-16", "ceiling": "-1dBTP", "true_peak_limit": "on", ...}}
3. Verify against the podcast spec
ferraus master verify "clean/*.wav" "clean/*.flac" --target podcast --json
{"ok": false, "result": [
{"input": "clean/ep01.fx.wav", "ok": false, "error": {"code": "verification_failed",
"message": "clean/ep01.fx.wav fails the podcast spec: loudness: integrated loudness -16.00 LUFS is +3.00 LU from the -19 LUFS target",
"hint": "ferraus master clean/ep01.fx.wav --target podcast -o fixed.wav", "exit_code": 9}}, ...]}
These episodes are mono, and the podcast spec is -16 LUFS for stereo but -19 LUFS for mono
(a mono file plays from both speakers, which sounds about 3 dB louder). Either normalize mono
episodes to -19 (--fx "normalize:lufs=-19") or make them stereo (--channels 2 on the fx
run) and keep -16.
4. Encode for publishing
ferraus convert "clean/*.wav" --format mp3 --bitrate 128k --outdir mp3 --json
mp3/ep01.fx.convert.mp3 mp3/ep02.fx.convert.mp3
5. Long batches
Run the batch in the background and collect it later:
ferraus fx "episodes/*.wav" --fx dehum --fx "normalize:lufs=-16" --outdir clean --async --json # {"ok": true, "job_id": "..."}
ferraus jobs wait <job_id> --json
Re-running the same command skips files whose output already exists with a matching sidecar
(warning reused_output, "result": {"reused": true}), so an interrupted batch can simply be
started again. To recompute anyway, pass --force --overwrite (--force alone stops at the
existing file with output_exists).