You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于FFmpeg按静音分割音频:如何精准生成12段有效音频?

基于FFmpeg的音频分割脚本优化问题

我需要自动将一首时长35分62秒的MP3音频分割为12段,该音频存在11个可见静音分割点,目标分割点大致为159、360、540秒等共11个。

使用基于FFmpeg的split_by_silence脚本测试时遇到以下问题:

  • 测试1:设置SD_PARAMS="-24dB"、MIN_FRAGMENT_DURATION="3",仅识别到5个分割点,生成6段音频;
  • 测试2:相同参数下识别到10个分割点,生成11段音频,但部分分割点间距过近,出现3秒、5秒的短音频,同时存在一段长达16分钟的超长音频;

添加MIN_SEGMENT_LENGTH=120参数后,仍存在分割点间距过短的问题,无法得到符合要求的12段音频。现寻求修改脚本及参数的方法,以自动识别有效分割点并生成12段符合长度要求的音频。


优化方案

1. 参数调整建议

  • 静音检测阈值:将SD_PARAMS调整为-30dB,更精准捕捉低音量的真实静音段,避免遗漏目标分割点;
  • 最小片段时长:将MIN_FRAGMENT_DURATION从3秒提高到10秒,过滤掉短时间的杂音停顿,减少无效分割点;
  • 最小分段长度约束:强化脚本逻辑,自动合并过短的分段区间,确保最终每段音频长度不低于120秒。

2. 脚本核心修改点

在现有脚本的分割点处理环节,新增短区间过滤逻辑:如果相邻分割点间距小于MIN_SEGMENT_LENGTH,则丢弃后一个分割点;若自动识别的分割点数量不足11个,结合预设的目标分割点进行补充,最终确保生成12段符合要求的音频。


修改后的完整脚本

# -----------------------
# SPLIT BY SILENCE
# Requirements:
#    ffmpeg
#    $ apt-get install bc
# How To Run:
# $ ./split_by_silence.sh "full_lowq.flac" %03d_output.flac

# output title format
OUTPUTTITLE="%03d_output.mp3"
# input audio filepath
IN="/mnt/e/martinradio/rips/vinyl/L.T.D. – Gittin' Down/lowquality_example.mp3"
# output audio filepath
OUTPUTFILEPATH="/mnt/e/folder/rips"
# ffmpeg option: 静音检测阈值
SD_PARAMS="-30dB"
# split option: 过滤短停顿的最小时长
MIN_FRAGMENT_DURATION=10
# 每段音频的最小长度(秒)
MIN_SEGMENT_LENGTH=120
# 预设目标分割点(自动识别不足时补充)
TARGET_SPLITS="159,360,540,720,900,1080,1260,1440,1620,1800,1980"

# -----------------------
# 步骤1:通过FFmpeg识别静音起始点作为分割候选
echo "_______________________"
echo "正在识别分割点..." >& 2
SPLITS=$( 
    ffmpeg -v warning -i "$IN" -af silencedetect="$SD_PARAMS",ametadata=mode=print:file=-:key=lavfi.silence_start -vn -sn  -f s16le  -y /dev/null \
    | grep lavfi.silence_start= \
    | cut -f 2-2 -d= \
    | perl -ne '
        our $prev;
        INIT { $prev = 0.0; }
        chomp;
        if (($_ - $prev) >= $ENV{MIN_FRAGMENT_DURATION}) {
            print "$_,";
            $prev = $_;
        }
    ' \
    | sed 's!,$!!'
)

# 步骤2:过滤过短的分割区间
IFS=',' read -ra AUTO_SPLITS <<< "$SPLITS"
FILTERED_SPLITS=()
LAST_SPLIT=0.0

for split in "${AUTO_SPLITS[@]}"; do
    diff=$(echo "$split - $LAST_SPLIT" | bc)
    if (( $(echo "$diff >= $MIN_SEGMENT_LENGTH" | bc -l) )); then
        FILTERED_SPLITS+=("$split")
        LAST_SPLIT=$split
    fi
done

# 步骤3:如果过滤后的分割点不足11个,补充预设目标点
if [ ${#FILTERED_SPLITS[@]} -lt 11 ]; then
    IFS=',' read -ra TARGET_ARRAY <<< "$TARGET_SPLITS"
    for target in "${TARGET_ARRAY[@]}"; do
        # 避免添加与已有分割点过于接近的点(误差5秒内)
        exists=0
        for s in "${FILTERED_SPLITS[@]}"; do
            if (( $(echo "abs($s - $target) < 5" | bc -l) )); then
                exists=1
                break
            fi
        done
        if [ $exists -eq 0 ]; then
            FILTERED_SPLITS+=("$target")
        fi
    done
    # 排序并去重(保留间隔5秒以上的点)
    FILTERED_SPLITS=($(printf "%s\n" "${FILTERED_SPLITS[@]}" | sort -g | awk 'BEGIN{prev=-10} {if ($1 - prev > 5) {print; prev=$1}}'))
    # 截取前11个分割点,确保生成12段音频
    FILTERED_SPLITS=("${FILTERED_SPLITS[@]:0:11}")
fi

# 转换为FFmpeg可用的逗号分隔字符串
FINAL_SPLITS=$(IFS=,; echo "${FILTERED_SPLITS[*]}")
echo "最终分割点列表= $FINAL_SPLITS"

# 检查最终分割点的间距
IFS=',' read -ra VALUES <<< "$FINAL_SPLITS"
for (( i=0; i<${#VALUES[@]}-1; i++ )); do
  diff=$(echo "${VALUES[$i+1]} - ${VALUES[$i]}" | bc)
  display_i=$((i+1))
  echo "$display_i. 分割点 ${VALUES[$i]} 与 ${VALUES[$i+1]} 的间距为 $diff 秒"
  if (( $(echo "$diff < $MIN_SEGMENT_LENGTH" | bc -l) )); then
    echo "       注意:该间距小于设定的最小分段长度 $MIN_SEGMENT_LENGTH 秒"
  fi
done

# 计算输出文件数量
num=$(( ${#VALUES[@]} + 1 ))
echo "_______________________"
echo "正在用FFmpeg导出 $num 段音频"

ffmpeg -i "$IN" -c copy -map 0 -f segment -segment_times "$FINAL_SPLITS" "$OUTPUTFILEPATH/$OUTPUTTITLE"

echo "完成。"

内容的提问来源于stack exchange,提问作者Martin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 01:29:58