把多段录音、多个音频片段、几段切割好的音频拼成一个完整文件——表面是”首尾相接”,底层是”统一格式 + concat filter”。这篇讲清两种拼接方式、为什么必须统一采样率、段间静音 / 交叉淡化的取舍。
为什么不能直接”首尾相接”
直接拼字节流的失败原因:
| 问题 | 例 |
|---|---|
| 文件头 / 尾 ID3 标签 | A 的 ID3v1 + B 的 ID3v2 在中间,播放器卡 |
| 帧不对齐 | A 是 192k 帧、B 是 128k 帧,长度不同 |
| 采样率不同 | A 是 44.1kHz、B 是 48kHz,B 段播放速度异常 |
| 声道不同 | A 是立体声、B 是单声道,B 段位移失真 |
结论:拼音频不能纯字节拼,必须先对齐到统一格式再合并。
ffmpeg 两种拼接方式
concat demuxer(流复制)
# list.txt:
# file 'a.mp3'
# file 'b.mp3'
ffmpeg -f concat -i list.txt -c copy out.mp3
前提:所有文件编码、采样率、码率、声道完全一致。
速度:极快(IO 限),1 小时音频几秒。
适用:把同一录音切成多段后再合回去。
不适用:用户随便上传几个 MP3。
concat filter(重编码)
ffmpeg -i a.mp3 -i b.mp3 \
-filter_complex "[0:a][1:a]concat=n=2:v=0:a=1[out]" \
-map "[out]" out.mp3
前提:无(任意输入)。
速度:中等(CPU 限),需要每段解码 + 拼接 + 重新编码。
适用:99% 的”用户上传几个文件想合并”场景。
本工具的实际做法
[输入 1] → aresample(44100) → asetpts → [a0]
[输入 2] → aresample(44100) → asetpts → [a1]
...
[a0][s0][a1][s1]...[aN] → concat filter → [out]
每段处理:
- 重采样到 44.1kHz / 立体声(即使原本就是也走一遍)
- 重置时间戳(asetpts=PTS-STARTPTS)
- 段间插入指定时长的静音段(s0 / s1 / …)
- concat filter 在样本层面拼接
好处:
- 任意采样率 / 码率 / 声道的输入都能拼
- 段间间隔精确到样本
代价:
- 慢(重编码)
- 输入有损 → 输出有损会有一次额外的有损损失
段间静音怎么选
| 间隔 | 听感 | 典型场景 |
|---|---|---|
| 0 秒 | 无缝 | 切割后合回、连续演奏录音 |
| 0.5 秒 | 短停顿 | 同播客多段录制 |
| 1 秒 | 常规停顿 | 多人采访、课程合并 |
| 2 秒 | 明显停顿 | 电台节目转换、多首歌单 |
默认推荐:1 秒(最不容易出错)。
段间静音 vs 交叉淡化
| 衔接方式 | 描述 | 听感 |
|---|---|---|
| 段间静音 | A 结束 → 静音 → B 开始 | 段落清晰分开 |
| 交叉淡化 | A 末段淡出 + B 开头淡入,重叠播放 | 自然过渡,无明显切换点 |
本工具仅支持段间静音。
想要交叉淡化:
- 各段先用淡入淡出工具单独加 fade in / out
- 用 0 秒 gap 合并 —— 但段没真重叠,效果有限
- 真正的 crossfade 去 Audacity 或命令行 ffmpeg
acrossfade
实际经验:
- 播客 / 课程:段间静音足够,无需 crossfade
- DJ 串歌:必须 crossfade
- 故事 / 朗读:1 秒静音听感最自然
拼接前的预处理建议
如果各段来自不同设备 / 录音环境,合并前建议:
| 不一致项 | 处理工具 |
|---|---|
| 各段响度不同 | 音量调整 → 标准化到 −16 LUFS |
| 各段格式不同 | 音频格式转换 → 统一 MP3 192k |
| 起点 / 结尾有杂音 | 音频剪辑 → 去头去尾 |
| 衔接太突兀 | 淡入淡出 → 各段加 50ms fade in / out |
完整流程示例:
3 段独立录音 (响度 / 格式不同)
↓ 音频格式转换 → 统一 WAV
↓ 音量调整 → 各段独立 LUFS −16
↓ 音频剪辑 → 各段去头去尾
↓ 音频淡入淡出 → 各段 50ms 头尾
↓ 音频合并 → 1 秒 gap 合并 → 输出 MP3 192k
拼接后体积估算
总时长 = 各段时长之和 + 间隔总和
按目标码率算:
| 码率 | 1 分钟 | 30 分钟 | 1 小时 |
|---|---|---|---|
| MP3 96k | 720 KB | 22 MB | 43 MB |
| MP3 192k | 1.4 MB | 43 MB | 86 MB |
| MP3 320k | 2.4 MB | 72 MB | 144 MB |
| WAV | 10 MB | 300 MB | 600 MB |
经验:合并大量音频时本地缓存压力大,超过 500MB 浏览器可能不稳——分批合并。
当前工具的真实边界
| 维度 | 实际能力 |
|---|---|
| 输入数量 | 多个(理论无上限,实际看浏览器内存) |
| 输入格式 | MP3 / WAV / M4A / AAC / FLAC / OGG / OPUS / WMA / AIFF / ALAC |
| 输出格式 | MP3 / WAV / AAC / M4A / FLAC(128/192/320 kbps) |
| 段间间隔 | 0 / 0.5 / 1 / 2 秒 |
| 拼接方式 | concat filter(重编码) |
| 顺序调整 | 拖拽列表项 |
| 删除某段 | 列表项删除按钮 |
| 内部统一 | 自动重采样到 44.1kHz / 立体声 |
不支持:
- 自定义任意秒数间隔(只有四档)
- 交叉淡化(crossfade)
- 流复制(concat demuxer)极速合并
- 段间自动响度标准化
- 反序合并(手动拖拽实现)
几个常见踩坑
1. 合并后发现顺序错了
上传顺序 = 默认合并顺序,错了拖拽调整。经验:上传前先按预期顺序排好文件。
2. 第 N 段开始有杂音
可能是该段文件损坏 / 编码异常。排查:先用音频格式转换把所有段统一成 MP3 192k 再合并。
3. 合并失败 / 浏览器卡死
段太多或总时长太长(> 1 小时 + 多段)→ 内存不够。解决:分批合并(先合 3 段、再合下 3 段、再合两个结果)。
4. 合并后响度不一致
段间响度差异大听起来不舒服 → 合并前先用音量调整工具把每段标准化到相同 LUFS。
一句话总结
音频合并底层是 ffmpeg concat filter(重编码),自动重采样到 44.1kHz / 立体声 → 任意采样率 / 格式都能拼;段间静音 0/0.5/1/2 秒(不支持任意秒数 / 不支持 crossfade);合并前各段先做响度统一才不会前大后小。