【rnnoise源码分析】compute_frame_feature函数

函数签名

/**
* @param st 全局context,存储一些上下文用的变量和结构体
* @param X  根据入参in生成的频域数据,其中在训练时,低频部分被全部清零
* @param P  是含有基频(pitch)部分语音信号生成的频域数据
* @param Ex [band](https://blog.csdn.net/mimiduck/article/details/106390000)能量数据 
* @param Ep 对应pitch信号数据的band能量
* @param Exp 信号能量与pitch能量的相关(correlation)值
* @param features rnnoise训练定义的feature
* @param in  输入数字信号,即时域信号
* @return gain 如果是训练数据有返回值
* */
int compute_frame_features(DenoiseState *st, 
											kiss_fft_cpx *X,
											kiss_fft_cpx *P,
											float *Ex, 
											float *Ep, 
											float *Exp, 
											float *features, 
											const float *in)

该函数主要做以下几件事情:

  1. 降采然后找pitch
    其中几个函数pitch_downsamplepitch_searchremove_doubling都是opus源码里的,作者基本照搬了过来,主要是为了找pitch

  2. 计算pitch信号band能量值,输入信号band能量值,以及根据两者计算出相关系数Exp

  apply_window(p);    // pitch数据应用window
  forward_transform(P, p); // 傅里叶变换计算pitch频域数据
  compute_band_energy(Ep, P); // 计算pitch部分的band能量
  compute_band_corr(Exp, X, P); // 计算信号频域与pitch频域的相关band能量系数

注意,这里的Bark频率划分为22个区域,所以NB_BANDS=22

Exp做标准化:
E x p i = E x p i / s q r t ( 0.001 + E x i ∗ E p i ) Exp_i = Exp_i/sqrt(0.001+Ex_i*Ep_i) Expi=Expi/sqrt(0.001+ExiEpi)

然后在做一次dct,实际上就是信号与pitch相关BFCC了

	for (i=0;i<NB_BANDS;i++) Exp[i] = Exp[i]/sqrt(.001+Ex[i]*Ep[i]);
  	dct(tmp, Exp);

然后填充feature的 NB_BANDS+2NB_DELTA_CEPS (22+26=34)到NB_BANDS+3*NB_DELTA_CEPS (40),填充后实际上是做了一些参数调整的。

  for (i=0;i<NB_DELTA_CEPS;i++) features[NB_BANDS+2*NB_DELTA_CEPS+i] = tmp[i];
  features[NB_BANDS+2*NB_DELTA_CEPS] -= 1.3;
  features[NB_BANDS+2*NB_DELTA_CEPS+1] -= 0.9;
  features[NB_BANDS+3*NB_DELTA_CEPS] = .01*(pitch_index-300);

而feature的1-NB_BANDS(22)是由log10(Ex)再做一次DCT后填充的,代码如下,

  logMax = -2;
  follow = -2;
  for (i=0;i<NB_BANDS;i++) {
    Ly[i] = log10(1e-2+Ex[i]);
    Ly[i] = MAX16(logMax-7, MAX16(follow-1.5, Ly[i]));
    logMax = MAX16(logMax, Ly[i]);
    follow = MAX16(follow-1.5, Ly[i]);
    E += Ex[i];
  }
 

  dct(features, Ly);

这个实际上就是输入信号的BFCC

22~33部分为delta差值,

  features[0] -= 12;
  features[1] -= 4;
  ceps_0 = st->cepstral_mem[st->memid];
  ceps_1 = (st->memid < 1) ? st->cepstral_mem[CEPS_MEM+st->memid-1] : st->cepstral_mem[st->memid-1];
  ceps_2 = (st->memid < 2) ? st->cepstral_mem[CEPS_MEM+st->memid-2] : st->cepstral_mem[st->memid-2];
  for (i=0;i<NB_BANDS;i++) ceps_0[i] = features[i];
  st->memid++;
  for (i=0;i<NB_DELTA_CEPS;i++) {
    features[i] = ceps_0[i] + ceps_1[i] + ceps_2[i];
    features[NB_BANDS+i] = ceps_0[i] - ceps_2[i];
    features[NB_BANDS+NB_DELTA_CEPS+i] =  ceps_0[i] - 2*ceps_1[i] + ceps_2[i];
  }
  /* Spectral variability features. */
  if (st->memid == CEPS_MEM) st->memid = 0;

cepstral_mem是一个8*22的数组,每一次feature里的值填充到ceps_0,然后这个数组会往下再做一次。

ceps_0是float指针,它指向的是ceptral_mem第一个NB_BANDS数组,然后每次与相邻的band数组相见,做出一个delta差值。

在这里插入图片描述
最后一个特性值,

for (i=0;i<CEPS_MEM;i++)
  {
    int j;
    float mindist = 1e15f;
    for (j=0;j<CEPS_MEM;j++)
    {
      int k;
      float dist=0;
      for (k=0;k<NB_BANDS;k++)
      {
        float tmp;
        tmp = st->cepstral_mem[i][k] - st->cepstral_mem[j][k];
        dist += tmp*tmp;
      }
      if (j!=i)
        mindist = MIN32(mindist, dist);
    }
    spec_variability += mindist;
  }
  features[NB_BANDS+3*NB_DELTA_CEPS+1] = spec_variability/CEPS_MEM-2.1;

所以,用于训练的特性值数组整体部署排布如下,
特征数组

  1. 34-40 输入信号与pitch相关BFCC
  2. 0-21 输入信号BFCC
  3. 0-5 / 22-27/ 28-33 delta BFCC
  4. 41 二阶delta
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值