← back to AgentAbrams

media/audio_mixer.py

74 lines

#!/usr/bin/env python3
"""
audio_mixer.py — Mix intro + voice + outro with loudness normalization.

Uses ffmpeg for broadcast-ready audio levels.

Usage:
    python3 media/audio_mixer.py <voice.mp3> <output.mp3>
"""

import subprocess
import sys
from pathlib import Path

INTRO = "assets/intro.mp3"
OUTRO = "assets/outro.mp3"


def mix_audio(voice_file: str, output_file: str):
    has_intro = Path(INTRO).exists()
    has_outro = Path(OUTRO).exists()

    Path(output_file).parent.mkdir(parents=True, exist_ok=True)

    if has_intro and has_outro:
        # Full mix: intro + normalized voice + outro
        cmd = [
            "ffmpeg", "-y",
            "-i", INTRO,
            "-i", voice_file,
            "-i", OUTRO,
            "-filter_complex",
            "[1:a]loudnorm=I=-16:TP=-1.5:LRA=11[voice];"
            "[0:a][voice][2:a]concat=n=3:v=0:a=1[aout]",
            "-map", "[aout]",
            "-c:a", "libmp3lame", "-b:a", "192k",
            output_file,
        ]
    elif has_intro:
        cmd = [
            "ffmpeg", "-y",
            "-i", INTRO, "-i", voice_file,
            "-filter_complex",
            "[1:a]loudnorm=I=-16:TP=-1.5:LRA=11[voice];"
            "[0:a][voice]concat=n=2:v=0:a=1[aout]",
            "-map", "[aout]",
            "-c:a", "libmp3lame", "-b:a", "192k",
            output_file,
        ]
    else:
        # Voice only with normalization
        cmd = [
            "ffmpeg", "-y",
            "-i", voice_file,
            "-af", "loudnorm=I=-16:TP=-1.5:LRA=11",
            "-c:a", "libmp3lame", "-b:a", "192k",
            output_file,
        ]

    print(f"Mixing audio → {output_file}")
    subprocess.run(cmd, check=True)
    print(f"Audio mixed: {output_file}")


def main():
    if len(sys.argv) < 3:
        print("Usage: audio_mixer.py <voice.mp3> <output.mp3>", file=sys.stderr)
        sys.exit(2)
    mix_audio(sys.argv[1], sys.argv[2])


if __name__ == "__main__":
    main()