SuperAPI AI Directory

bgm

General & other

View repository

Updated 10-16 00:21

About this skill

Background Music Skills

Royalty-Free Music Sources

Source Website Features
Pixabay Music https://pixabay.com/music/ Completely free, no attribution required
Free Music Archive https://freemusicarchive.org/ CC licensed, some require attribution
Uppbeat https://uppbeat.io/ Free version has a monthly limit
Mixkit https://mixkit.co/free-stock-music/ Completely free

Recommended: Pixabay Music (no attribution required, free for commercial use)

Music Style Selection

Video Type Recommended Style Keyword Search
Tech Journey Epic, Cinematic "epic cinematic", "technology"
Product Introduction Corporate, Upbeat "corporate", "business"
Tutorial Video Ambient, Soft "ambient", "background"
Launch Event Inspiring, Motivational "inspiring", "motivational"
Creative Content Modern, Electronic "modern", "electronic"

FFmpeg Audio Mixing

Basic Mixing (Voiceover Priority)

# 将 BGM 与配音混合,BGM 音量降低
# 配音密集时推荐 volume=0.05,配音稀疏时可用 0.08-0.10
ffmpeg -i voiceover.mp3 -i bgm.mp3 \
  -filter_complex "[1:a]volume=0.05[bgm];[0:a][bgm]amix=inputs=2:duration=first[out]" \
  -map "[out]" mixed_audio.mp3

With Fade-In/Fade-Out Effects

# BGM 淡入3秒,淡出3秒
ffmpeg -i voiceover.mp3 -i bgm.mp3 \
  -filter_complex "
    [1:a]volume=0.05,afade=t=in:st=0:d=3,afade=t=out:st=82:d=3[bgm];
    [0:a][bgm]amix=inputs=2:duration=first[out]
  " \
  -map "[out]" mixed_audio.mp3

Complete Audio Processing Workflow

# 1. 处理 BGM(循环、淡入淡出、降低音量)
ffmpeg -stream_loop -1 -i bgm_original.mp3 \
  -t 85 \
  -af "volume=0.05,afade=t=in:st=0:d=3,afade=t=out:st=82:d=3" \
  bgm_processed.mp3

# 2. 混合配音和 BGM
ffmpeg -i voiceover.mp3 -i bgm_processed.mp3 \
  -filter_complex "[0:a][1:a]amix=inputs=2:duration=first[out]" \
  -map "[out]" final_audio.mp3

# 3. 音量标准化
ffmpeg -i final_audio.mp3 \
  -af "loudnorm=I=-16:TP=-1.5:LRA=11" \
  final_audio_normalized.mp3

Volume Balance Recommendations

Core Principles

Voiceover Clarity Takes Priority: BGM must exist as "background" and must never overpower the voiceover. This is especially important when the vocal frequency range (85-255Hz fundamental + harmonics) overlaps with the BGM.

Audio Type Relative Volume Description
Voiceover 1.0 (100%) Primary audio, keep at original volume
BGM (dense voiceover) 0.05-0.08 Ensure the voiceover is clearly distinguishable
BGM (sparse voiceover) 0.08-0.12 When there are many gaps in the voiceover
BGM (no voiceover sections) 0.15-0.25 Intro/outro/transitions
BGM (pure music sections) 0.30-0.50 Can be increased when there is no voiceover

Volume Selection Decision Tree

视频有配音吗?
├── 是 → 配音密度高吗(>70%时间有语音)?
│   ├── 是 → BGM volume = 0.05-0.06 (推荐 0.05)
│   └── 否 → BGM volume = 0.08-0.10
└── 否 → BGM volume = 0.25-0.40

Testing Method

Before final rendering, extract a 30-second segment containing voiceover for testing:

# 快速测试不同音量
for vol in 0.04 0.05 0.06 0.08; do
  ffmpeg -y -i bgm.mp3 -i voiceover.mp3 \
    -filter_complex "[0:a]volume=$vol[bgm];[1:a][bgm]amix=inputs=2[out]" \
    -map "[out]" -t 30 test_vol_$vol.mp3
done

Dynamic Volume Control (Advanced)

# 配音出现时自动降低 BGM(侧链压缩效果)
ffmpeg -i voiceover.mp3 -i bgm.mp3 \
  -filter_complex "
    [0:a]asplit=2[vo][vo_sidechain];
    [1:a]volume=0.3[bgm];
    [bgm][vo_sidechain]sidechaincompress=threshold=0.02:ratio=8:attack=50:release=500[bgm_ducked];
    [vo][bgm_ducked]amix=inputs=2:duration=first[out]
  " \
  -map "[out]" ducked_audio.mp3

Smart BGM Length Calculation

Problem: The Drawbacks of Mindless Looping

A simple -stream_loop -1 will cause:

  • BGM cut off at unnatural positions
  • Music rhythm does not match video content
  • Unnatural listening experience at loop seams

Solution: Scene-Aware BGM Selection

Strategy 1: Choose Music of an Appropriate Length

PreferBGM whose duration is close to or slightly longer than the video:

Video Duration Recommended BGM Duration Strategy
< 60s 60-90s Single-track trimming
60-120s 90-180s Single-track trimming + fade-out
120-300s 180-360s Single track or carefully designed seams
> 300s Multiple tracks spliced together Switch at scene transitions

Strategy 2: Calculate Based on Scene Structure

# 场景时间配置
SCENES = {
    "opening": {"start": 0, "duration": 8},    # 片头
    "scene1": {"start": 8, "duration": 14},    # 场景1
    "scene2": {"start": 22, "duration": 16},   # 场景2
    "scene3": {"start": 38, "duration": 14},   # 场景3
    "scene4": {"start": 52, "duration": 20},   # 场景4
    "closing": {"start": 72, "duration": 13},  # 片尾
}

def calculate_bgm_strategy(scenes: dict, bgm_duration: float, video_duration: float):
    """
    计算 BGM 使用策略

    Returns:
        dict: 包含 trim_end, need_loop, loop_point 等信息
    """
    if bgm_duration >= video_duration:
        # BGM 够长,直接裁剪
        return {
            "strategy": "single_trim",
            "trim_end": video_duration,
            "need_loop": False
        }

    # BGM 不够长,找最佳循环点(场景边界)
    scene_boundaries = sorted([s["start"] for s in scenes.values()])

    # 找到最接近 BGM 时长的场景边界
    best_loop_point = min(
        scene_boundaries,
        key=lambda x: abs(x - bgm_duration) if x <= bgm_duration else float('inf')
    )

    return {
        "strategy": "scene_aware_loop",
        "loop_point": best_loop_point,
        "bgm_duration": bgm_duration,
        "loops_needed": math.ceil(video_duration / best_loop_point)
    }

Strategy 3: Dynamic Volume Adapted to Scenes

Use different BGM volumes for different scenes:

# 场景音量配置
SCENE_VOLUMES = {
    "opening": 0.15,    # 片头无配音,音量较高
    "scene1": 0.05,     # 有配音场景
    "scene2": 0.05,
    "scene3": 0.05,
    "scene4": 0.05,
    "closing": 0.12,    # 片尾配音较少
}

def generate_volume_filter(scenes: dict, volumes: dict, fps: int = 30):
    """生成 FFmpeg 音量自动化滤镜"""
    points = []
    for name, scene in scenes.items():
        start_frame = scene["start"] * fps
        vol = volumes.get(name, 0.05)
        points.append(f"volume=enable='between(t,{scene['start']},{scene['start']+scene['duration']})':volume={vol}")

    return ",".join(points)

FFmpeg Scene-Aware Mixing

# 使用 aeval 实现场景动态音量
# 片头(0-8s): 0.15, 正片(8-72s): 0.05, 片尾(72-85s): 0.12

ffmpeg -i voiceover.mp3 -i bgm.mp3 \
  -filter_complex "
    [1:a]volume='if(lt(t,8),0.15,if(lt(t,72),0.05,0.12))':eval=frame,
    afade=t=in:st=0:d=3,
    afade=t=out:st=82:d=3[bgm];
    [0:a][bgm]amix=inputs=2:duration=first[out]
  " \
  -map "[out]" mixed_audio.mp3

Best Practices

  1. Prioritize BGM with a suitable duration: avoid looping; choose music with a duration >= the video length
  2. If looping is necessary: set the loop point at scene transitions rather than at the end of the music
  3. Fade in/out at scene boundaries: Use crossfade at loop seams
  4. Dynamic volume: Intro and outro can be slightly higher, lower during dense voiceover sections

Remotion Dynamic Volume Control (Recommended)

In addition to using FFmpeg preprocessing, dynamic volume control can also be implemented directly in Remotion. This approach is more flexible and easier to debug.

Basic usage: scene-aware volume

import { Audio, staticFile, useCurrentFrame, useVideoConfig } from "remotion";
import { SCENES } from "./config/scenes";

// 场景音量配置
const SCENE_VOLUMES = {
  opening: 0.15,   // 片头无配音,音量较高
  scene1: 0.05,    // 有配音场景
  scene2: 0.05,
  scene3: 0.05,
  scene4: 0.05,
  closing: 0.12,   // 片尾配音较少
};

// 动态音量函数
const getDynamicVolume = (frame: number, fps: number): number => {
  const currentTime = frame / fps;

  // 遍历场景找到当前所在场景
  for (const [sceneName, scene] of Object.entries(SCENES)) {
    const sceneEnd = scene.start + scene.duration;
    if (currentTime >= scene.start && currentTime < sceneEnd) {
      return SCENE_VOLUMES[sceneName as keyof typeof SCENE_VOLUMES] ?? 0.05;
    }
  }
  return 0.05; // 默认音量
};

// 在组件中使用
export const VideoWithDynamicBGM: React.FC = () => {
  const { fps } = useVideoConfig();

  return (
    <>
      {/* 配音 - 固定音量 */}
      <Audio src={staticFile("audio/voiceover.mp3")} volume={1} />

      {/* BGM - 动态音量 */}
      <Audio
        src={staticFile("audio/bgm.mp3")}
        volume={(frame) => getDynamicVolume(frame, fps)}
      />
    </>
  );
};

Advanced usage: smooth transitions

import { interpolate } from "remotion";

const getSmoothVolume = (frame: number, fps: number): number => {
  const currentTime = frame / fps;

  // 片头 (0-8s): 0.15 -> 片头结束前0.5s开始降低
  if (currentTime < 7.5) return 0.15;
  if (currentTime < 8) {
    return interpolate(currentTime, [7.5, 8], [0.15, 0.05]);
  }

  // 正片 (8-71.5s): 0.05
  if (currentTime < 71.5) return 0.05;

  // 正片结束 -> 片尾 (71.5-72s): 平滑过渡到 0.12
  if (currentTime < 72) {
    return interpolate(currentTime, [71.5, 72], [0.05, 0.12]);
  }

  // 片尾 (72-85s): 0.12
  return 0.12;
};

Component template

// components/DynamicBGM.tsx
import React from "react";
import { Audio, staticFile, useVideoConfig } from "remotion";

interface DynamicBGMProps {
  /** BGM 文件路径 (相对于 public/) */
  src: string;
  /** 场景音量配置 */
  sceneVolumes: Record<string, number>;
  /** 场景时间配置 */
  scenes: Record<string, { start: number; duration: number }>;
  /** 是否启用平滑过渡 (默认 true) */
  smoothTransition?: boolean;
  /** 过渡时长 (秒, 默认 0.5) */
  transitionDuration?: number;
}

export const DynamicBGM: React.FC<DynamicBGMProps> = ({
  src,
  sceneVolumes,
  scenes,
  smoothTransition = true,
  transitionDuration = 0.5,
}) => {
  const { fps } = useVideoConfig();

  const getVolume = (frame: number): number => {
    const currentTime = frame / fps;

    for (const [sceneName, scene] of Object.entries(scenes)) {
      const sceneEnd = scene.start + scene.duration;
      if (currentTime >= scene.start && currentTime < sceneEnd) {
        return sceneVolumes[sceneName] ?? 0.05;
      }
    }
    return 0.05;
  };

  return (
    <Audio
      src={staticFile(src)}
      volume={(frame) => getVolume(frame)}
    />
  );
};

FFmpeg vs Remotion comparison

Aspect FFmpeg preprocessing Remotion dynamic control
Debugging convenience ⚠️ Requires reprocessing audio ✅ Real-time preview
Flexibility ⚠️ Fixed once ✅ Adjust anytime
Rendering performance ✅ No extra overhead ⚠️ Minor overhead
File size ⚠️ Premixed files are large ✅ Separate storage
Recommended scenario Final delivery Development and debugging

Recommended workflow:

  1. During development, use Remotion dynamic control for quick debugging
  2. Once final parameters are determined, use FFmpeg preprocessing to generate the delivery version

Python script template

#!/usr/bin/env python3
"""
背景音乐处理脚本
"""

import subprocess
from pathlib import Path

def process_bgm(
    bgm_path: str,
    voiceover_path: str,
    output_path: str,
    video_duration: float,
    bgm_volume: float = 0.05,  # 默认低音量,确保配音清晰
    fade_duration: float = 3.0
):
    """
    处理背景音乐并与配音混合

    Args:
        bgm_path: BGM 文件路径
        voiceover_path: 配音文件路径
        output_path: 输出文件路径
        video_duration: 视频总时长(秒)
        bgm_volume: BGM 相对音量 (0.0-1.0)
        fade_duration: 淡入淡出时长(秒)
    """
    fade_out_start = video_duration - fade_duration

    # 构建 filter_complex
    filter_complex = f"""
        [1:a]aloop=loop=-1:size=2e+09,atrim=0:{video_duration},
        volume={bgm_volume},
        afade=t=in:st=0:d={fade_duration},
        afade=t=out:st={fade_out_start}:d={fade_duration}[bgm];
        [0:a][bgm]amix=inputs=2:duration=first[out]
    """.replace("\n", "").replace("  ", "")

    cmd = [
        "ffmpeg", "-y",
        "-i", voiceover_path,
        "-i", bgm_path,
        "-filter_complex", filter_complex,
        "-map", "[out]",
        output_path
    ]

    subprocess.run(cmd, check=True)
    print(f"✓ 混合音频输出: {output_path}")

def normalize_audio(input_path: str, output_path: str):
    """音量标准化到 -16 LUFS"""
    cmd = [
        "ffmpeg", "-y",
        "-i", input_path,
        "-af", "loudnorm=I=-16:TP=-1.5:LRA=11",
        output_path
    ]
    subprocess.run(cmd, check=True)
    print(f"✓ 标准化输出: {output_path}")

# 使用示例
if __name__ == "__main__":
    process_bgm(
        bgm_path="public/audio/bgm_epic.mp3",
        voiceover_path="public/audio/synced_voiceover.mp3",
        output_path="public/audio/mixed_audio.mp3",
        video_duration=85,
        bgm_volume=0.05,  # 配音密集时推荐 0.05
        fade_duration=3.0
    )

    normalize_audio(
        input_path="public/audio/mixed_audio.mp3",
        output_path="public/audio/final_audio.mp3"
    )

Remotion integration

Use the mixed audio in a Remotion component:

import { Audio, staticFile } from "remotion";

export const FinalVideo: React.FC = () => {
  return (
    <>
      {/* 使用混合后的音频(配音 + BGM) */}
      <Audio src={staticFile("audio/final_audio.mp3")} volume={1} />

      {/* 或者分开控制 */}
      <Audio src={staticFile("audio/synced_voiceover.mp3")} volume={1} />
      <Audio
        src={staticFile("audio/bgm.mp3")}
        volume={(f) => {
          // 淡入淡出控制
          const fps = 30;
          if (f < 3 * fps) return (f / (3 * fps)) * 0.12;
          if (f > 82 * fps) return ((85 * fps - f) / (3 * fps)) * 0.12;
          return 0.12;
        }}
      />
    </>
  );
};

Workflow

1. 确定视频风格 → 选择音乐关键词
2. 从免版权网站下载 BGM
3. 处理 BGM(循环/裁剪到视频时长)
4. 添加淡入淡出效果
5. 与配音混合(配音优先)
6. 音量标准化
7. 集成到视频

Common issues

Issue Solution
BGM too loud and interferes with voiceover Lower volume to 0.04-0.06 (0.05 recommended during dense voiceover)
BGM not long enough Prefer longer music; if looping is necessary, splice at scene boundaries
Fade in/out too abrupt Increase fade_duration to 4-5 seconds
Inconsistent volume after mixing Use the loudnorm filter to normalize
Style mismatch Choose a more suitable BGM again
Loop seam is obvious Use crossfade or set the loop point at a scene transition