新知mfcc语音识别esp8266继电器开关灯(成为一个DIYer,八)

我们现在完整走一遍mfcc,假定,我们的‘开’音记录在bt4096[4096]数组之中,要搞定这个,前面博客有例子程序,我是一路走下来的,我们取样率8k/s,但我们只取4096字节,一个字节范围是0-255,记录波形以128为中心,上下波动,比如,一般噪音都在128取值附近,所以当我们要想知道一个波形的振幅A,用A=bt4096[i]-128来表达。

1,预加重,公式用程序语言表达,bt4096[i]=bt4096[i+1]-0.95*bt4096[i];以前经常处理图像用这样的公式,bt4096[i]=bt4096[i+1]-bt4096[i],他表示,两个像素之间颜色变化有多大,越小,说明比较平坦,越大,表示,这可能是边界上的点,想必是可以借鉴的吧(整幅图像如此处理,就是差分图像,他的意思是说,我只想看到整图的变化部分)!

2,首先应该是分帧,预加重抢了先,那就这样吧!256字节一帧,128为帧移动,那4096字节能有多少帧呢?4096/128=32,那么当取31帧时,已经只剩128字节了,不够取,所以我们干脆取30帧即可。

  for (int i = 0; i < 30; i++)

 {          
             double[] frame = new double[256];
             for (int j = 0; j < 256; j++)
             {                                 
                     frame[j] = bt4096[i *128+ j ]  ;                                      
             }

。。。。。。

}

3,汉明窗,别人讲的够多了,我们直接上代码:

for (int i = 0; i < 30; i++)

 {   。。。。。。

           // windowing,加汉明窗
             for (int j = 0; j < 256; j++)
             {
                 frame[j] *= 0.54 - 0.46 * Math.Cos(2 * Math.PI * j / (256- 1));
             }
             /////////////////////

}

4,快速傅里叶变换fft,前面博客有,我就过了,用别人的,结果和我一样,已经验证。

需要注意的是,以下都砸在这个循环里for (int i = 0; i < 30; i++){。。。。。。}

5,分26组和计算s(m)=ln∑Hm(k)*|Xa(k)|^2,前面都有了,代码连起来的样子如下(这以下都是一帧frame【256】的处理,懂了,再放入循环里去):

  double[] filterbank = new double[26];
            
            double[] Xr = new double[256];
          
            double[] Xi = new double[256];

            for (int i = 0; i < 256; i++)
            {
                Xr[i] = frame[i];//实部
                Xi[i] = 0;//虚部
            }

            //FFT//别人的fft已经验证过了,256=2^8(fft条件)
            FFT(1, 256, Xr, Xi);//计算完,实部和虚部被重新写入值

   //以下melfre用自己的202002062131
            //假定300hz到8k,分26组
            double min_Mel_frequency = Mel_Scale(1, 300);     
            double max_Mel_frequency = Mel_Scale(1, 8000);
            double 梅尔间隔 = (max_Mel_frequency - min_Mel_frequency) / (26 + 1);
            double[] 等距梅尔频率组 = new double[28];
            double[] 等同mel组映射频率组fanli = new double[28];
            int[] fft128样本点256映射temp = new int[28];

            等距梅尔频率组[0] = min_Mel_frequency;
            等同mel组映射频率组fanli[0] = 300;
            for (int i = 1; i < 28; i++)
            {
                等距梅尔频率组[i] = min_Mel_frequency + 梅尔间隔 * i;
                等同mel组映射频率组fanli[i] = Mel_Scale(-1, 等距梅尔频率组[i]);
                fft128样本点256映射temp[i] = (int)((128 + 1) * 等同mel组映射频率组fanli[i] / 8000);
            }
            fft128样本点256映射temp[0] = (int)((128 + 1) * 等同mel组映射频率组fanli[0] / 8000);//记住,要取整数202002061940
            //下一步,可以求Hm(k)了。m是26组filterbanks,k在4到128之间。因为是从300开始的。

               //画一个256(128*2)的图出来?ok
          
            for (int k = 4; k < 128; k++)
            {
                //    //取平方值
                double power = (Xr[k] * Xr[k] + Xi[k] * Xi[k]) / 256;

                for (int m = 0; m < 26; m++)
                {
                    double[] frequency_boundary = new double[3];

                    frequency_boundary[0] = fft128样本点256映射temp[m + 0];
                    frequency_boundary[1] = fft128样本点256映射temp[m + 1];
                    frequency_boundary[2] = fft128样本点256映射temp[m + 2];

                    if (frequency_boundary[0] <= k && k <= frequency_boundary[1])
                    {
                        double hmk = (k - frequency_boundary[0]) / (frequency_boundary[1] - frequency_boundary[0]);                     
                        filterbank[m] += power * hmk;                      
                    }
                    else if (frequency_boundary[1] <= k && k <= frequency_boundary[2])
                    {
                        double hmk = (frequency_boundary[2] - k) / (frequency_boundary[2] - frequency_boundary[1]);                    
                        filterbank[m] += power * hmk;                   
                    }
                }
            }      

6,取对数(自然对数):   
            for (int i = 0; i < 26; i++)
            {
                filterbank[i] = Math.Log(filterbank[i]);
            }

7,进行离散余弦变换,他们说,在江湖混,有一天是要还回来的(离散余弦变换)。或许还回来的是惊喜,是衣锦还乡。

  //DCT//dct不用验证了,因为他的fft是ok的。
            DCT(1, 26, filterbank);//离散余弦变换实质是fft的实部计算,非常省心。

            //获取MFCC特征向量
            for (int i = 0; i < 13; i++)
            {
                feature_vector[i] = filterbank[i];//特征在这里边,乡音未改。
            }

好了打完,收工,代码走完,这是不带画图的(画图,耗时,有时还容易出错),画图是给自己学习和别人学习看的,至于去试一试esp8266WiFi开关灯,前面博客代码已经有了,如果你通过对一个实时语音4096字节mfcc计算,如果是开音特征,就让你的台灯亮起来吧!

相信你的台灯也可以亮起来!千呼万唤始出来。

发布了66 篇原创文章 · 获赞 12 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/ganggangwawa/article/details/104362794