SuperAPI AI Directory

normalize-loudness

General & other

View repository

Updated 10-15 21:54

About this skill

normalize-loudness

Purpose

Normalize the loudness of a video or audio file using ffmpeg's two-pass loudnorm filter (EBU R128). Most social platforms target around -14 LUFS integrated with a -1 dB true-peak ceiling and an LRA cap around 11. This atom is the canonical way to hit those targets — replaces ad-hoc alimiter chains in per-ad polish scripts.

Two-pass is required (not single-pass) because the second pass uses the measured statistics from the first to apply linear normalization with no dynamic distortion.

Inputs

  • --input <path> — mp4 / mov / wav / mp3 (required)
  • --output <path> — destination file (required)
  • --target-i <lufs> — integrated LUFS (default -14)
  • --target-tp <dbtp> — true-peak ceiling in dBTP (default -1)
  • --target-lra <lu> — loudness range cap (default 11)
  • --platform <name> — preset that overrides the three above. Supported: social (default, -14/-1/11), broadcast (-23/-2/7), podcast (-16/-1/11)

Workflow

  1. Verify ffmpeg and ffprobe are on PATH.
  2. Pass 1 — measure: run ffmpeg -i <input> -af loudnorm=I=<I>:TP=<TP>:LRA=<LRA>:print_format=json -f null - and parse the JSON tail of stderr for input_i, input_tp, input_lra, input_thresh, target_offset.
  3. Pass 2 — apply: run ffmpeg -i <input> -af loudnorm=I=<I>:TP=<TP>:LRA=<LRA>:measured_I=...:measured_TP=...:measured_LRA=...:measured_thresh=...:offset=...:linear=true:print_format=summary -c:v copy <output> (video stream is copied untouched).
  4. Parse the second-pass summary for the measured-after values.
  5. Write manifest.json and verification.md alongside the output.

Output

  • <output> — normalized file (video copy + remuxed audio).
  • manifest.json:
    {
      "input": "...",
      "output": "...",
      "target": { "I": -14, "TP": -1, "LRA": 11 },
      "measured_before": { "I": ..., "TP": ..., "LRA": ..., "thresh": ... },
      "measured_after":  { "I": ..., "TP": ..., "LRA": ..., "thresh": ... },
      "platform": "social",
      "status": "ok"
    }
    
  • verification.md — short pass/fail report: integrated LUFS within ±0.5 of target, TP under ceiling.

Quality checks

  • Output integrated LUFS within ±0.5 of target.
  • Output true-peak strictly below ceiling.
  • Video stream byte-identical to input (verify via ffprobe stream md5).
  • Pass-1 JSON parsed correctly (no NaN values in the measured stats).

Failure modes

  • Single-pass loudnorm — produces dynamic-range distortion. Always two-pass.
  • ffmpeg not on PATH or wrong build — loudnorm needs ffmpeg ≥ 3.1.
  • Input audio is silent / DC — measured_I returns -inf; script must short-circuit and emit status: skipped_silent.
  • Very short clips (< 3 s) — loudnorm window is unreliable; emit a warning in manifest.

Example

scripts/loudnorm.sh \
  --input peloton/ads/video-03-ironman-comeback/edits/master-final.mp4 \
  --output peloton/ads/video-03-ironman-comeback/edits/master-normalized.mp4 \
  --platform social

Consumed by: /polish (audio-loudness branch), auto-refine (audio sub-loop), edit-video (Phase 9 final mix).