基本计算方法可以参考Mel滤波器组的设计与实现(基于MATLAB和Python)这篇文章,从数学公式到实现程序都很完整。
快速计算是因为一旦确定了截止频率和滤波器组数,滤波器的系数可以一次求出来,那么如何快速的实现计算呢?首先我们确定输入的是什么,滤波器的输入是经过fft变换之后的BIN,长度是 2 n − 1 + 1 2^{n-1}+1 2n−1+1,如果做256(n=8)的 fft,输入数组的长度就是129。
X ( n ) = F F T ( X ( t ) ) = [ X 1 , X 2 , . . . . . X 2 n − 1 + 1 ] X(n)=FFT(X(t))=[X_1,X_2,.....X_{2^{n-1}+1}] X(n)=FFT(X(t))=[X1,X2,.....X2n−1+1]
这种方法利用矩阵的乘法运算,假设 输入序列 长度为 N, 梅尔滤波器组数L,构造一个Mel滤波器的系数矩阵 M N ∗ L M_{N*L} MN∗L,矩阵的每一列对应Frequency bin的权重值保留,其他位置填充0,如下面的数组 :
/*000*/{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
/*001*/{0.70424,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
/*002*/{0.61587056,0.38412944,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
/*003*/{0,0.95637744,0.04362256,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
/*004*/{0,0.33359797,0.66640203,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
/*005*/{0,0,0.72805736,0.27194264,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
/*006*/{0,0,0.14240339,0.85759661,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
/*007*/{0,0,0,0.58317275,0.41682725,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
/*008*/{0,0,0,0.03243114,0.96756886,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
/*009*/{0,0,0,0,0.51258737,0.48741263,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
/*010*/{0,0,0,0,0,0.99499423,0.00500577,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
/*011*/{0,0,0,0,0,0.50795775,0.49204225,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
/*012*/{0,0,0,0,0,0.02092128,0.97907872,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
/*013*/{0,0,0,0,0,0,0.56167114,0.43832886,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
/*014*/{0,0,0,0,0,0,0.10366818,0.89633182,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
/*015*/{0,0,0,0,0,0,0,0.66678804,0.33321196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
/*016*/{0,0,0,0,0,0,0,0.23608783,0.76391217,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
/*017*/{0,0,0,0,0,0,0,0,0.81698896,0.18301104,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
/*018*/{0,0,0,0,0,0,0,0,0.41196391,0.58803609,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
/*019*/{0,0,0,0,0,0,0,0,0.00693886,0.99306114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
/*020*/{0,0,0,0,0,0,0,0,0,0.62564476,0.37435524,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
.......................................
用矩阵乘法
M E L 1 ∗ L = X 1 ∗ N ∗ M N ∗ L MEL_{1*L} =X_{1*N}*M_{N*L} MEL1∗L=X1∗N∗MN∗L
遂得出MEL谱值。
上述计算方法在目标系统支持矩阵运算时非常方便,但还有些处理器不支持,另外此方法需要一个比较大内存来放置0,非常浪费,在嵌入式系统里可以用C语言指针数组的方法,高效的实现计算。这里除了将Mel滤波器的系数顺序存放在一段内存中,需要一个二级指针数组指向每段系数存放的首地址;另外需要一个频率映射上下边界指针数组,从FFT BIN的对应频率点取得对应的数值与权重点乘后累加。示意图如下,前两行示意相同颜色的方框
参与一个循环的运算。这里提到的一点是权重二级指针数组的寻址可以使用二维表格方式,代码将非常有趣。