响度测量算法主要有三种:
Peak:只检测峰值
RMS:均方根运算,输出 平均响度 和 最大响度(最大刻度为 零),即使音频的峰值响度超过了 零,依然显示为 零;适用于正态分布模型(否则会有失真)
EBU R.128:主要特点:1. 用两个 Gate 过滤了音频中的 “静默” 场景;2. 检测方法更符合人类听觉感知;3. 算法不是很复杂,避免对设备造成过大负担;4. 未来随着技术的革新与设备的升级,会有更加优秀的算法出现。
本篇主要介绍EBU R.128响度测量原理和怎样使用ffmpeg进行响度测量。
EBU R.128方法介绍
EBU R128是一个基于欧洲广播联盟 (EBU) R128标准的响度测量算法。这个标准旨在提供一种统一的、国际性的音频响度测量方法,以解决由于广播内容响度差异大造成的听觉不一致问题。EBU R128的主要特点包括:
响度单位:EBU R128使用响度单位(Loudness Unit,简称LU)来衡量音频的感知响度,其中1 LU等于1 分贝。标准还定义了LUFS(Loudness Units relative to Full Scale),一种相对于满量程的响度单位,用于数字音频。
K权重滤波器:EBU R128使用K权重滤波器来模拟人耳对不同频率声音的感知敏感度。这是一种频率加权滤波器,强调人耳对某些频率范围的敏感度。
综合响度(Integrated Loudness,I):这是整个节目或音频段的平均响度。它是基于整个音频内容的长期平均值计算出来的。
短期响度(Short-term Loudness,S):它是在较短的时间窗口(例如3秒)内计算的响度。这有助于识别音频中的短暂响度变化。
瞬时响度(Momentary Loudness,M):这是基于更短的时间窗口(例如400毫秒)计算的响度,用于捕捉音频的即时变化。
响度范围(Loudness Range,LRA):这是一个衡量音频响度变化范围的指标,通常用于评估音频的动态范围。
真峰值(True Peak):这是测量音频波形的最大振幅,以确保音频不会超过特定的峰值限制,从而避免失真。
在实际应用中,ebur128算法会对音频信号进行K权重滤波,然后基于定义的时间窗口计算响度。这通常涉及取样本的平方,应用时间加权,然后计算对数平均值以获取响度值。这个过程会根据音频的持续时间和内容的动态范围反复进行,以生成综合响度、短期响度、瞬时响度和响度范围等指标。
ffmpeg怎样分析响度
ffmpeg命令打印分析EBU R.128
ffmpeg -i input_audio.wav -filter_complex ebur128 -f null -
在这个例子中:
-i input_audio.wav
-filter_complex ebur128,使用ebur128算法进行响度分析
下面是ffmpeg的输出结果:
t:时间,单位是秒;
M:瞬时响度(Momentary Loudness, M),单位是lufs
I:综合响度(Integrated Loudness, I),单位是lufs
LRA:响度范围(Loudness Range, LRA),单位lu

ffmpeg命令打印分析音量
ffmpeg -nostats -i input_audio.wav -af "volumedetect" -f null -

ffplay 可视化的方法
ffplay -f lavfi -i "amovie=input_audio.wav,ebur128=video=1:meter=18 [out0][out1]"
主绘图区域包含短期响度(3秒分析),右侧的刻度用于瞬时响度(400毫秒)

EBU R.128算法详解
原理
综合响度 的计算:


综合响度 的计算分两个阶段,由两个门限(Gate)控制。
第一阶段:若 瞬时响度 (即400ms 的 “块”)大于 绝对门限阈值(-70 LUFS),则此 “块” 获准通过 绝对门限 ,参与下一步的计算,这些 “块” 的综合响度为 一阶综合响度 ,即上图中的 绿线 Lk,no gate = -26 LUFS,将此 一阶综合响度 减去 10 LU 后的值设为 相对门限阈值(-36 LUFS),即上图中的 红线 。
第二阶段:若 瞬时响度 (即400ms 的 “块”)大于 相对门限阈值 (-36 LUFS),则此 “块” 获准通过 相对门限 ,参与下一步的计算,这些 “块” 的综合响度为 二阶综合响度 ,即上图中的 黄线 Lk,gated = -25.2 LUFS,此为最终 综合响度。

响度范围 的计算需要使用到 “ 综合响度 的计算的第一阶段生成的 一阶综合响度 ”,上图中 绿线,减去 20 LU 得到 紫线(-41.8 LUFS),以 紫线 为 响度范围的相对门限阈值,剔除前 10% 和 后 5%,所遗留区域的范围即为 响度范围,上图中 橙色区域 。
ffmpeg中ebur算法实现详解
代码路径
“libavfilter/f_ebur128.c”
代码结构解析
数据结构解析
宏定义和常量:
定义了用于预滤波器和RLB滤波器的系数
// 预滤波器的系数定义
#definePRE_B0 1.53512485958697
#definePRE_B1 -2.69169618940638
#definePRE_B2 1.19839281085285
#definePRE_A1 -1.69065929318241
#definePRE_A2 0.73248077421585
// RLB滤波器的系数定义
#defineRLB_B0 1.0
#defineRLB_B1 -2.0
#defineRLB_B2 1.0
#defineRLB_A1 -1.99004745483398
#defineRLB_A2 0.99007225036621
定义了其他的常量,如最大通道数、静音门限和直方图的大小
// 定义静音门限(LUFS)
#defineABS_THRES -70
// 定义上限响度门限
#defineABS_UP_THRES 10
// 定义直方图精度
#defineHIST_GRAIN 100
// 计算直方图大小
#defineHIST_SIZE ((ABS_UP_THRES - ABS_THRES) * HIST_GRAIN + 1)
结构体定义:
hist_entry:存储响度直方图的条目
// 直方图条目的结构体定义
struct hist_entry { int count; // 对应值出现的次数
double energy; // 能量 E = 10^((L + 0.691) / 10)
double loudness; // 响度 L = -0.691 + 10 * log10(E)
};
integrator:存储集成响度计算所需的各种参数和缓存。
// 积分器结构体定义
struct integrator {
double *cache[MAX_CHANNELS]; // 滤波后样本的窗口(N毫秒)
int cache_pos; // 缓存数组中最后添加的元素位置
double sum[MAX_CHANNELS]; // 最后N毫秒滤波后样本的和(缓存内容)
int filled; // 如果缓存完全填满则为1,否则为0
double rel_threshold; // 相对阈值
double sum_kept_powers; // 高于绝对阈值的功率和
int nb_kept_powers; // 高于绝对阈值的求和次数
structhist_entry*histogram; // 功率的直方图,用于计算LRA和I
};
EBUR128Context:主结构体,包含所有滤波器状态、配置和缓存。
// EBU R128上下文结构体定义
typedef struct{
const AVClass *class; // AVClass上下文,用于日志和选项 ...
...
// 视频相关的字段
int do_video; // 如果启用视频输出则为1,否则为0
int w, h; // 视频输出的大小
struct rect text; // 左侧LU图例的矩形
struct rect graph; // 中心主图的矩形
struct rect gauge; // 右侧计量器的矩形
AVFrame *outpicref; // 视频输出的参考帧,定期更新 ...
...
// 音频相关的字段
int nb_channels; // 输入的通道数
double *ch_weighting; // 通道加权映射
...
// 滤波器缓存
double x[MAX_CHANNELS * 3]; // 每个通道的3个输入样本缓存
...
// 积分器实例
struct integratori400; // 400ms积分器,用于瞬时响度(M)和综合响度(I)
struct integratori3000; // 3秒积分器,用于短期响度(S)和响度范围(LRA) ...
} EBUR128Context;
功能函数:
包括绘制文本、绘制线条、配置视频输出等辅助函数。
核心处理函数:
filter_frame:这是核心处理函数,负责处理每一帧音频,计算响度,并更新视频输出(如果启用)。
初始化和清理函数:
init:初始化滤波器,设置相关参数。
uninit:释放分配的资源。
格式和配置函数:
query_formats:设置支持的音频格式和采样率。
config_audio_input和config_audio_output:配置音频输入和输出链接的属性。