我给 AI 写了个「音频编译器」:把「听起来还行」变成 PASS/FAIL

先讲一件让我印象很深的事

我用一套脚本流水线批量做音频内容,几十个文件,全部渲染完成,全部听过一遍,「没问题」。

交付时被退回来了。

原因不是音质,不是时长,不是响度------是一个编号规则。对方要求所有内容文件按顺序连续编号,从 1 开始。我的文件从 2 开始编,因为我把片头和片尾也算进了序号里。

他们那边看到的是「第 1 部分缺失」。

这件事最扎心的地方不是规则本身,而是:每一个环节都没有报错。 文件渲染成功了,我听过没问题,上传成功了,页面也收下了。问题出在「没有人用机器的方式验证过一遍」。

从那之后我想明白一件事:这条流程里最不可靠的环节,是「人来判断行不行」。


一、AI 没有耳朵

给人用的音频软件------Audacity、Audition、剪映------它们的设计有一个隐含前提:用户有耳朵。

波形图是给你看的,播放按钮是给你听的,「这儿有点爆」是你的耳朵告诉你的。整个验证闭环建立在人的感官上。

AI 没有这条通道。

它能渲染出一段音频,但它不知道自己做得对不对。你让它「把整条音轨的响度控制在 -20 dBFS 附近」,它会照做,然后回复你「已完成」。至于完成得是否达标,它不知道,你也不知道------直到某个下游环节把你拦下来。

于是分工变得很别扭:AI 干了 95% 的活,然后人肉去核对那 95% 到底对不对。而核对往往比生产还慢。

问题的本质不是「AI 做不好音频」,而是「音频这个领域没有为 AI 设计的反馈通道」。


二、换个思路:把音频后期当成编译

我想要的其实不是「更好用的音频工具」,而是一个编译器。

编译器都有什么?三样东西:

编译器 音频后期
源码 一份声明式的工程文件(IR)
编译 一次确定性的构建(render)
诊断信息 一份 PASS/FAIL 报告 + 可执行的修法

音频后期完全可以这样拆。于是有了 acomp:

markdown 复制代码
写 IR (project.yml)  →  acomp render  →  产物 + 测量报告(数字 / PASS-FAIL)
      ↑                                              │
      └────────── 读报告,改一行,再来一次 ────────────┘

IR 是源码,render 是编译,报告是编译器的诊断信息。音频库、有声书、播客、课件------只要是一堆片段拼成一条音轨,都归它管。


三、它长什么样

没有波形图,没有「你听一下」。render 打出来的就是验收结果:

scss 复制代码
ID        DUR(s)      PEAK(dB)   RMS(dB)    LEAD      TRAIL     RESULT   FAILED TARGETS
--------  ----------  ---------  ---------  --------  --------  -------  --------------------------
S01       9.00        -16.99     -20.51     0.00      1.00      PASS
S02       9.00        -0.91      -4.44      0.00      1.00      FAIL     peak_dbfs,rms_dbfs
S03       8.00        -16.99     -20.00     0.00      0.00      PASS

MASTER    25.20       -0.91      -8.84      0.00      0.00      FAIL     peak_dbfs,rms_dbfs

结果:2/3 段通过,master FAIL,整体 NEEDS FIX

这一段就是核心:把「听起来还行」换成 PASS/FAIL。

IR 本身是一份普通的 YAML:

yaml 复制代码
version: 1

project:
  name: chapter_01
  out_dir: ./out

targets:                           # 统一 [min, max],任一端可写 null
  total_duration: [300.0, 420.0]
  peak_dbfs: [null, -3.0]
  true_peak_dbfs: [null, -3.0]     # 真峰值,交付验收口径
  rms_dbfs: [-23.0, -18.0]
  lead_silence: [null, 0.25]
  trail_silence: [null, 0.25]
  segment_checks: [peak_dbfs, rms_dbfs]

segments:
  - id: S01
    src: raw/S01.wav
    trim: {start: 0.0, end: null}  # 绝对时间戳,不是「裁多少秒」
    gain_db: 0.0
    pad_after: 1.0                 # 段尾留 1s 静音
    crossfade_next: 0.4

output:
  master: chapter_01_master.wav
  limiter:
    enabled: true
    limit_db: -3.2
    attack: 5
    release: 50
  mp3:
    bitrate: 192k

它有三个好处,是命令行脚本给不了的:

  • 可以进 git。 音频工程的「状态」不再散落在命令历史和一些没人看得懂的 shell 脚本里,而是一份可 review 的文本。改了什么,git diff 说话。
  • 可以 diff。 两份 IR、或者两份测量报告,acomp diff 给出的是语义级差异(「MASTER 从 FAIL 变 PASS」),不是文本差异。
  • 可以被程序生成。 这条后面单独讲,是最有用的一条。

四、三个我觉得关键的决策

1. 退出码要有语义

一般脚本要么「成功=0 / 失败=1」。但在音频验收里,「构建失败」和「构建成功但数字不达标」是完全不同的两件事。所以 acomp 的退出码是:

退出码 含义
0 全部达标
1 构建成功,但有 FAIL(不达标 ≠ 失败:产物在,只是数字不过关)
2 出错(源文件缺失、参数非法等),stderr 是结构化 JSON

这个区分看起来小,作用很大:1 是可以自动重试的------读报告、改一个参数、重跑;2 是必须人来处理的。自动化流程里,这两条路的差别是「能不能无人值守」。

2. 错误信息要可执行

出错时不该让调用方猜。举一个真实的错误对象:

json 复制代码
{
  "code": "SRC_MISSING",
  "what_failed": "段落 S02 的源文件不存在:/work/raw/S02.wav",
  "minimal_fix": "把文件放到该路径,或改 segments[1].src",
  "retryable": false
}

注意 minimal_fix:它写的是「把 X 改成 Y」,不是「请检查配置」。

这是给程序(尤其是 AI)读的字段------它必须知道下一刀切在哪里,而不是知道「出了个问题」。

3. 先预演,再动手

validate(只校验 IR)和 plan(探测素材、预估时长、判定目标是否可达)都不写盘。

所以整个流程可以是这样:AI 先跑 plan,零成本地知道「照这个参数做到什么程度」;有把握了再 render。

这一条对 AI 协作特别重要------它把「试错」的成本压到了零。


五、一段真实的算术:限幅不是审美,是数学

这个坑我觉得值得单独说,因为它经常被当成「音质问题」,其实是个代数问题。

常见交付要求是两条:Peak ≤ -3 dBFS ,同时 RMS ≥ -23 dBFS。

把这两条放在一起看:

scss 复制代码
crest(峰均比) = peak - rms ≤ (-3) - (-23) = 20 dB

而素材的 crest 是由内容决定的,调增益只能整体平移,改不了 crest。

所以当某段素材的 crest 已经超过 20 dB 时,任何纯增益方案在数学上都不可能同时满足这两条------必须压峰值(限幅或压缩)。这跟母带审美没有关系,是算术。

acomp plan 会在 render 之前做这道判定:测出每段的真实内容窗口、算出 master 的 crest,如果判定「纯增益无解」,直接告诉你「只能靠限幅达成」,并给出建议的增益和限幅阈值。

bash 复制代码
python acomp.py probe <file>     # 看 crest:peak 减 rms 超过 20 就别指望调增益能过
python acomp.py plan  proj.yml   # 动手之前先问「能不能到」

省下的是「先试一遍、发现不行、再试一遍」那种来回。

顺带说两个会骗人的口径

① 采样峰值 ≠ 真峰值。 mp3 编码后,采样点之间的真实波形峰值会比采样峰值高 0.2~0.4 dB。你拿 astats 测出「peak −3.24,达标」,用 ebur128 一测真峰值是 −2.9------其实超了。所以限幅阈值要按真峰值倒推,不能只给采样峰值留余量。

② RMS ≠ LUFS。 同一段音频,RMS −22.4,LUFS 可能是 −17.8,差 4 dB 以上。下游按哪个口径验收,就在 targets 里写哪个,别拿 RMS 当 LUFS 用。

这两个坑的共同点是:它是「口径问题」而不是「质量问题」,但后果跟质量问题一样------被退回。


六、IR 是数据,所以可以被程序生成

这是整套设计里我最满意的一条。

因为 IR 就是一份普通的数据结构,所以它不需要人手写------可以程序生成。

仓库里带了一个例子 tools/batch_fix.py:

bash 复制代码
# 只体检:打印每个文件的电平/静音/需要补多少增益,不写任何音频
python tools/batch_fix.py --src /path/to/delivery_package

# 体检 + 渲染修复版 + 按交付口径复检
python tools/batch_fix.py --src /path/to/delivery_package --render

它做的事是:扫一遍目录,测每个文件的 peak / rms / crest / 首尾静音,算出该补多少增益、该裁多少静音,然后生成对应的 IR 交给 render 去构建。

全程没有人打开过音频编辑器。

这就是「状态是文本」带来的复利:你写的是一份数据,于是它能被脚本生成、被程序批量修改、被 AI 直接产出。


七、怎么跑起来

不装任何东西,不需要手动配 ffmpeg(由 imageio-ffmpeg 自带,也不需要 ffprobe):

bash 复制代码
# 国内网络建议用 Gitee 镜像
git clone https://gitee.com/dusknook/acomp.git  && cd acomp
git clone https://github.com/dusknook/acomp.git && cd acomp

python demo.py      # Windows 双击 demo.bat;macOS/Linux 用 ./demo.sh

demo.py 会自动建虚拟环境、装依赖,然后跑完整个闭环:造 3 段合成测试音(其中一段故意过响)→ validate → plan → render(过响版:报 FAIL,退出码 1)→ 改一行 gain_db → render(全部 PASS,退出码 0)→ diff 两份报告。

整个过程十几秒,跑完你就看到「一个不达标的音频是怎么被定位、被改掉、被复验的」。

依赖只有 pyyaml / numpy / imageio-ffmpeg 三个,MIT 许可,随便商用改。


八、已知的限制(不藏着)

  • 只做非线性编辑里最常见的几种操作:裁剪 / 增益 / 淡入淡出 / 静音填充 / 交叉淡化。降噪、EQ、压缩这类没进 IR------它们参数空间太大,目前建议先交给 ffmpeg 滤镜字符串。
  • 真峰值是逐段实测(ebur128),不是经验公式估算,所以 plan 会慢一点,想跳过可以加 --no-feasibility。
  • 可选的对齐检查依赖 faster-whisper,没装就静默跳过,不影响主流程。
  • 目前只支持音频;视频容器、多轨混音不在范围内。

最后

这个工具解决的不是「音频做得好不好听」,而是**「音频做得对不对,能不能不靠耳朵就知道」**。

如果你也在用脚本或 AI 批量处理音频,被「交付标准对不上、来回返工」烦过,欢迎试试看,也欢迎把你的 IR 例子提上来------交流真实案例比点赞有用得多。

仓库:

有 issue 和 PR 都欢迎。

相关推荐
在世修行1 小时前
干货:信号槽
python·信号槽
Wx-bishekaifayuan1 小时前
springboot生活易购超市管理系统54086-计算机课程设计、毕业设计
spring boot·后端·python·django·课程设计·express·旅游
数字融合1 小时前
透明化数字孪生智慧养老平台运维服务项目
人工智能·python·virtualenv
smj2302_796826521 小时前
解决leetcode第4064题至多一次取反能被k整除的最长子数组II
python·算法·leetcode
happylifetree2 小时前
Python01-08:练习
python
空奈qwq2 小时前
ANN 全连接神经网络入门指南:从神经元到深度学习的第一步
人工智能·python·深度学习·神经网络·算法·机器学习
维克兜率天3 小时前
【维克】弹性策略:用“乖离率“捕捉超跌反弹
android·开发语言·python·深度学习·kotlin·量化
SamChan903 小时前
PDF页面旋转与CropBox陷阱:译文回填位置总是错位的根因实测
python·ai·pdf·wpf
程序员清风3 小时前
Java 智能体开发:从对话接口到任务执行
java·人工智能·python