講座の本文は現在、中国語版のみです。各講のタイトルと導入文、メニューやボタンは日本語で表示しています。

第 07 講全 23 講約 10 分

周波数の分析をせず、あの曲線だけを見て何がわかるのか?

前の講で処理の流れはできましたが、「フレームごとに計算する」の欄がまだ空いています。この講では時間領域の 3 つの答えでそこを埋めます。振幅包絡(このフレームの最大値はいくつか)、RMS=二乗平均平方根(このフレーム全体の強さはどれくらいか)、ゼロ交差率(このフレームで中心線を何回横切るか)。どれも周波数の分析はいらず、曲線そのものを見るだけです。この 3 つは入れ替えのきく名前ではなく、3 つの違う問いです。それぞれに独自の公式、弱点、使いどころがあります。この講では 3 つの公式を、8 個の数からなる小さなフレームでそれぞれ手計算します。ペンがあれば自分で確かめられます。

导读: 上一课把流水线搭好了,「逐帧计算」那一格还空着。这一课填上时域这条的三个答案:振幅包络(这一帧最高多少)、均方根(这一帧整体多强)、过零率(这一帧穿过中线多少次)。三个都不需要任何频率分析,只看那条曲线本身。它们不是三个可以互换的名字,而是三个不同的问题——每一个都有自己的公式、自己的弱点、自己的用武之地。这一课把三个公式各拿一个八个数的小帧手算一遍,你拿笔就能核对。

第 7 课属于第 2 组「把录音变成可计算片段」,本组共 5 课

三个时域特征

时域特征就是直接从那条曲线算出来、不做频率分析的特征。本课讲三个最常用的,先各用一句话说清它们分别是什么:

  • 振幅包络指的是:一帧里最高的那个数有多大。英文缩写 AE。
  • 均方根指的是:一帧整体上有多强,帧里每个数都参与。英文缩写 RMS。
  • 过零率指的是:一帧里那条曲线穿过中线多少次。英文缩写 ZCR。
振幅包络、均方根、过零率各问一个不同的问题

图 1:三个特征各问一个不同的问题。三个答案都在手上,才知道这一帧是「短促一下」还是「一直很响」。

下面三节各讲一个。为了能拿笔核对,三个公式都用同一个八个数的小帧算:

这一帧的样本数记作 。

振幅包络:这一帧里最高的那个数

定义:一帧里所有样本中,最大的那个振幅值。

公式看着吓人,逐项拆开只有四样东西:

符号是什么
第 帧的振幅包络——要算的那个结果
第 个样本的振幅——原始的那串数
帧长,一帧有多少个样本
第 帧的第一个样本的编号
第 帧的最后一个样本的编号

所以这条式子读出来就是:在第 帧那一段里,从头扫到尾,取最大的那个。 对每一帧都这么算一次,把结果按时间连起来,就是一条贴着波形外沿走的线。

振幅包络公式逐项拆解,以及在八个数的小帧上的结果

图 2:上面是公式各部分对应什么,下面是这八个数上的实际结果。注意最右边那两个数不一样。

照公式写代码会漏掉一半

拿这八个数跑一遍:

照公式直接取最大    0.7
先取绝对值再取最大  0.9

两个数不一样,差 0.2。

原因是 :它是负的,但它离中线的距离比 更远。这一帧真正的峰是 0.9,不是 0.7。

“最大振幅”里的振幅本来就指“离零有多远”,不带符号。所以写成代码时必须补上绝对值:

ae = np.max(np.abs(frame))     # 不能写成 np.max(frame)

漏掉 abs,一帧里最深的那个谷就永远看不见——而对打击类的声音来说,谷往往和峰一样深。

优点、缺点、用在哪

  • 能粗略反映响度。 峰越高,通常听着越响。
  • 对离群值敏感。 一帧里只要有一个异常大的数,整帧的结果就被它一个人决定了。一次爆音、一次点击噪声,都会让这一帧的 AE 冲到顶。
  • 常见用途:起音检测(找出每一下是什么时候开始的)、音乐曲风分类。

均方根:这一帧整体有多强

AE 只看一个数。RMS 让这一帧里的每一个数都参与。

这条式子从里往外读,正好三层:

这一层在算什么
第 个样本的能量
这一帧所有样本的能量加起来
除以样本数,得到平均能量
再开平方

名字就是倒着念这三步:Root(根)· Mean(均)· Square(方)。

均方根的三步计算:平方、求平均、开方

图 3:从八个原始数走到 0.5256,中间三步。

为什么要平方,又为什么要开回来

拿那八个数直接求平均:

直接求平均 = 0.0625

几乎是零。因为正负互相抵消了——这一帧明明在剧烈振动,直接平均却量不出任何强弱。

平方就解决了这件事:平方之后全是正数,没有抵消。

① 每个数平方
   0.09 0.49 0.04 0.81
   0.16 0.01 0.25 0.36
② 全部加起来   2.21
③ 除以 K=8     0.2762
④ 开平方       0.5256

那为什么还要开回来?因为平方之后量级变了(0.2762 和原始那些数不在一个尺度上)。开平方把它换回和原始样本一样的量级,这样 RMS 和 AE 才能画在同一张图上直接比。

优点、缺点、用在哪

  • 能反映响度,而且比 AE 更贴近「整体感觉」。
  • 比 AE 更不怕离群值。 一个孤立的尖峰要和其余 个数一起平均,被稀释掉了。
  • 常见用途:音频分段(把一段长录音切成若干有意义的片段)、音乐曲风分类。

过零率:这一帧穿过中线多少次

前两个都在量「有多大」。ZCR 完全不管大小,只数次数。

定义:信号穿过横轴的次数。

过零率的定义:数波形穿过横轴的次数

图 4:绿点是曲线穿过中线的位置。数它们有几个,就是这一帧的过零次数。

写成公式:

核心是那个 ,叫符号函数,它把一个数压成三种情况之一:

然后看相邻两个样本的符号一样不一样:

  • 同号,比如 ,取绝对值除以 2 还是 0——没穿过。
  • 异号,比如 ,取绝对值除以 2 得 1——穿过了一次。

那个 就是干这个用的:把「差 2」换算成「1 次」。

手算这八个数

原始   0.3  0.7 -0.2 -0.9
       0.4  0.1 -0.5  0.6
符号    +1   +1   -1   -1
        +1   +1   -1   +1
相邻异号? 0 1 0 1 0 1 1

加起来:这一帧穿过中线 4 次。

一个必须知道的口径差异

手算到“4 次”就结束了——它给的是一个次数。但实际使用时,还要除以帧长换成比例,好让不同帧长之间能比。除数有两种写法:

除以相邻对的个数 7 = 0.5714
  ← 本课程 soundlab 的口径
除以帧长 8       = 0.5000
  ← librosa 的口径

这不是我推的,脚本里真调了一次 librosa 让它自己给:

librosa 实际算出来 0.5000
等于 4/8,确实是除以帧长
zero_crossings(pad=False)
数出 4 次,和上面的手算一致

所以呢? 两个口径差一个 。帧长 1024 时这个差只有 0.1%,可以忽略;但同一个数据集必须自始至终用同一个口径,否则两批数字没法直接比。第 09 课把手写实现和 librosa 对齐时,第一个要对上的就是这个除数。

优点、缺点、用在哪

  • 完全不受音量影响。 把整段乘以 10,穿过中线的次数一次也不会变——下面有实测。
  • 不能单独用来判断音高。 噪声也可能频繁过零;录音里很小的电气抖动还会制造额外的交点。
  • 常见用途:
    • 分辨打击乐和有音高的乐音——打击声穿零又多又不规则。
    • 单音高估计——只有一个音在响时,过零次数和音高有直接关系。
    • 语音的清浊音判断——「s」「f」这类摩擦音的过零率远高于元音。

实验:换成真实录音,三条曲线各画各的

运行 lesson07_three_time_features.py,对 debussy.wav 的前 3 秒逐帧算三个数:

66150 个样本,帧长 1024 帧移 512
切出 128 帧
       最小     最大     平均
AE   0.1315  0.2685  0.1808
RMS  0.0426  0.0888  0.0647
ZCR  0.0450  0.0929  0.0654
同一段录音的波形与三条时域特征曲线

图 5:四条线共用同一条时间轴。最上面是原始波形,下面三条是逐帧算出的 AE、RMS、ZCR。

三条曲线都是 128 个点、共用同一条时间轴。但它们不是彼此的缩放版——把三条各自除以自己的最大值之后算相关系数:

AE 和 RMS   0.794
AE 和 ZCR  -0.322
RMS 和 ZCR -0.552

所以呢? AE 和 RMS 高度同向(0.794),因为两个都在量「这一帧振动得多大」。ZCR 和它们是负相关,而且明显弱一截——它数的是穿过中线的次数,和振动多大根本不是一回事。

要点不是这个负号有多大,而是:ZCR 量的是另一件事,不能拿它替代 AE 或 RMS,反过来也不行。

最直接的证据:把整段乘以 10

      原样    ×10 后   倍数
AE   0.1808  1.8081  10.00
RMS  0.0647  0.6465  10.00
ZCR  0.0654  0.0654   1.00

AE 和 RMS 严丝合缝地跟着变了 10 倍。ZCR 一动不动。

所以呢? 乘一个正数不会让曲线多穿过中线一次,也不会少一次。这条性质说明 ZCR 和「录得多响」完全无关——第 09 课比较语音和噪声时要用到它。

这一课得到了什么,下一课接着讲什么

AERMSZCR
问什么最高多少整体多强穿线多少次
用几个样本只用最大的那一个全部 个全部相邻对
怕离群值吗很怕不太怕不怕
受音量影响吗是是完全不受
常见用途起音检测、曲风分类音频分段、曲风分类打击乐对乐音、单音高估计、清浊音判断

三个公式讲完了,但还没有一行真正能跑在整首曲子上的实现。上面那些数是脚本算的,脚本里的函数还没拆开看过。

下一课从头实现振幅包络:读进三段音乐、画出波形、手写 AE、把帧编号换算成时间、最后把包络叠在波形上——完整走一遍,看看三种曲风的包络长得有多不一样。