【rnnoise源码分析】band能量计算

rnnoise中有个函数compute_band_energy,用于计算band的能量值。这里有几个概念需要理清楚。

基本概念

在看源码之前,我们要弄懂几个概念,不然还真没法看下去了。讲一下什么是frequency band和frequency bin。

band

band字面意思是频率带,是一组频率的范围。大家都知道,音声都是由频率的,而频率范围很广。人耳可知的频率范围就在20到20000hz。如果这些频率都参与计算,计算量过大。特别是一些实时系统,希望延迟低,计算量小,为了减少运算量,同时也根据人耳对低频敏感,高频不敏感的特性,不均等的对频谱作为划分,划分的每一小段就是一个band。

大名鼎鼎的Mel Scale,梅尔划分,给出了一个公式。
m=2595log10(1+f700) m=2595 * \log10(1+\frac f {700})
它是根据人耳对音高的不同敏感度做划分的。

之后的 MFCC(梅尔倒谱系数) 就是基于此做出来的:
音频加窗FFT后,使用Mel滤波器滤波,然后对数运算DCT,得到MFCC。 (这个不是本文讨论重点,网上帖子也很多,有需要可自行查阅

rnnoise的前端是参考opus来做的,opus的band划分是参考bark scale来做的。

bark scale是根据人耳对不同响度来划分的,bark是划分了24个band;opus在它的基础上略微做了调整,增加了低频部分band的宽度,同时减少了2个band,所以opus scale是22个band。

opus bands

bin

bin的本意是容器,箱子。frequency bin的意思就是FFT的时候将频率分成的组。
理论上来讲,如果我们研究20000hz以内的频率,理想情况下我们会有20000组,从1hz到20000hz,每一组就是一个bin。
根据Nyquist-Shannon采样原理,我们至少要做40000hz的采样。然后观测时长至少为1s,那么最大频率20000hz才可以被观测到。显然这样的处理不符合实时系统。

一般实时系统dsp分析的一帧为10ms(也有20ms,50ms等其他,不讨论这个不同),那么10ms下40000Hz就会有400个sample。这400个sample数据在做fft后会得到200个数据(实际上是201个),这些数据就是bin。说白了就是用200个数据标识1~20000Hz频率,那么每个bin代表了20000/200=100个频率。
即,
第一个bin是1~100
第二个bin是101~200
.……

那你肯定会问了,那bin和band不是一样吗,都标识一个范围。表面上来看是这样,但是两者意义不一样。

band是人为根据需要做的划分,它是固定的,不是平均的;而bin是为了减少计算量,快速实时处理做的妥协,它不是固定的,根据实际情况而定,但是平均的

常量定义

在rnnoise源码中,定义了eband5ms来表示22个band的常量数组。如下,

static const opus_int16 eband5ms[] = {
/*0  200 400 600 800  1k 1.2 1.4 1.6  2k 2.4 2.8 3.2  4k 4.8 5.6 6.8  8k 9.6 12k 15.6 20k*/
  0,  1,  2,  3,  4,  5,  6,  7,  8, 10, 12, 14, 16, 20, 24, 28, 34, 40, 48, 60, 78, 100
};

上面这22个数字是何含义?

注释中数字是opus band的划分
常量数字的设计就有一些弯弯绕了,我上面说了那些个概念,也是为了阐述这一块而作铺垫。
请看我的分析。

rnnoise使用采样率是48000(可能是偷懒,兼容opus),10ms一帧的采样,那么一帧480samples,windows采用的两倍的帧长,就是960. 这些源码里都有,自己看一下。

那么计算一下bin的大小,计算如下
48000/960=50

ok,那么现在有bin=50,以及eband5ms里的两排数字,请找出他们的关系。(这简直就是一道公务员考试里的数学逻辑题嘛)
节约大家时间,我直接写出答案

bineband5ms[i]4=band[i] bin * eband5ms[i]*4 = band[i]

为什么要这样设计呢?

为的是实现三角滤波
三角滤波

每个band会被划分为两个相邻值差*4个频率点,即

(eband5ms[i+1]-eband5ms[i])<<2

前面9个都是4,后面的会变多,因为后面的band会宽一些。

能量计算公式
E(k)=X(k)2 E(k) = |X(k)|^2

那么就那前两个bin来看,每一个bin被分为4份。
bin0的4个子频率能量被按比例累加到Energy 0和 Energy 1中。(Energy 0指的是band0的能量和)
这个比例我们假设为 w,它是跟band和bin中子频率k相关。
所以E(b)的计算公式(E(b)就是band b的能量和)如下
E(b)=0kwb(k)X(k)2 E(b) = \displaystyle\sum_0^k w_b(k) |X(k)|^2

函数源码解析

compute_band_energy源码如下,基本该说的我都在上面说了。可以一目了然了。

void compute_band_energy(float *bandE, const kiss_fft_cpx *X) {
  int i;
  float sum[NB_BANDS] = {0};
  for (i=0;i<NB_BANDS-1;i++)
  {
    int j;
    int band_size;
    band_size = (eband5ms[i+1]-eband5ms[i])<<FRAME_SIZE_SHIFT;
    for (j=0;j<band_size;j++) {
      float tmp;
      float frac = (float)j/band_size;
      tmp = SQUARE(X[(eband5ms[i]<<FRAME_SIZE_SHIFT) + j].r);
      tmp += SQUARE(X[(eband5ms[i]<<FRAME_SIZE_SHIFT) + j].i);
      sum[i] += (1-frac)*tmp;
      sum[i+1] += frac*tmp;
    }
  }
  sum[0] *= 2;
  sum[NB_BANDS-1] *= 2;
  for (i=0;i<NB_BANDS;i++)
  {
    bandE[i] = sum[i];
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章