MFCC算法讲解及实现(matlab)

article/2025/10/20 22:18:48

史上最详细的MFCC算法实现(附测试数据)

  • 1.matlab安装voicebox语音包
  • 2.MFCC原理讲解
  • 3.MFCC算法设计实现(matlab)
    • 3.1 .wav格式语音文件提取【x(200000*1)】
    • 3.2 预加重【x(200000*1)】
    • 3.3 分帧{S(301*1103)}
    • 3.4 加窗{C(301*1103)}
    • 3.5 傅里叶变换
    • 3.6 梅尔滤波器
    • 3.7 离散余弦变换
    • 3.8 注(2022年5月3日修)
  • 4.总结
    • 4.1更新(2021.5.9)
    • 4.2.实验中diguashao.wav自取(2022年5月3日更新)
  • 5.参考文献,资料

1.matlab安装voicebox语音包

这里该包的安装我直接附上我们师姐写过的一篇文章,里边的介绍很详细:
戳这里!!!跳转到文章链接地址

2.MFCC原理讲解

整个MFCC过程大致可以分为以下几步:

1.音频文件读取(最好是.wav文件)
2.预先加重
3.分帧
4.加窗
5.傅里叶变换(当是2的N次方时,可以使用FFT快速傅里叶变换)
6.梅尔滤波器组
7.离散余弦变换DCT
在这里插入图片描述

3.MFCC算法设计实现(matlab)

3.1 .wav格式语音文件提取【x(200000*1)】

\qquad 在matlab中,使用函数audioread函数来读取本地wav文件,这里要注意的是,采样频率一般为8000Hz和16000Hz,采样频率需要大于真实信号最大频率的2倍,才不会导致频谱混叠。

clc;
clear;
[x,fs]=audioread('diguashao.wav');%读取wav文件

这里我们用于测试的数据的采样频率 f s f_s fs 44100,这个是由 audioread函数默认决定的。

3.2 预加重【x(200000*1)】

\qquad 为了避免在后边的FFT操作中出现数值问题,我们需要加强一下高频信息,因为一般高频能量比低频小。其预加重函数如下所示:
y ( n ) = x ( n ) − α ⋅ x ( n − 1 ) y(n)=x(n)-\alpha \cdot x(n-1) y(n)=x(n)αx(n1)
其中 α \alpha α一般取值为 0.97 、 0.95 0.97、0.95 0.970.95

%预加重y=x(i)-0.97*x(i-1)
for i=2:200000y(i)=x(i)-0.97*x(i-1);
end
y=y';%对y取转置

然后我们再来对比一下原始文件和预加重后的数据差异
在这里插入图片描述
我们可以看到整个的数据其幅度范围是有所减小的,但是可以看得出来,高频部分的缩小倍数是小于低频部分的缩小倍数的。

3.3 分帧{S(301*1103)}

\qquad 我们要对语音数据做傅里叶变换,将信息从时域转化为频域。但是如果对整段语音做FFT,就会损失时序信息。因此,我们假设在很短的一段时间t内的频率信息不变,对长度为t的帧做傅里叶变换,就能得到对语音数据的频域和时域信息的适当表达。例如我们这里的采样点数为200000个点,如果真的这样做的话,就很麻烦了,于是我们在语音分析中引入分帧的概念,将原始语音信号分成大小固定的N段语音信号,这里每一段语音信号都被称为一帧。
\qquad 但是,如果我们这样分帧的话,帧与帧之间的连贯性就会变差,于是我们每一帧的前N个采样点数据与前一帧的后N个采样点数据一样。其原理图大致如下所示:
在这里插入图片描述
\qquad 对于整个采样点数据可以分为多少帧以及帧与帧之间交叉的采样点个数N,不是随便分的,一般来说帧长设置为 25 m s 25ms 25ms,帧移设置为 10 m s 10ms 10ms,对于我这次的仿真,其帧数和帧长数值如下:
帧数 = f s ⋅ 0.025 = 44100 ⋅ 0.025 = 1103 ( 个采样点 ) 帧移 = f s ⋅ 0.001 = 44100 ⋅ 0.01 = 441 ( 个采样点 ) \quad 帧数=f_s \cdot 0.025=44100 \cdot0.025=1103(个采样点)\\ \quad \\ \quad 帧移=f_s \cdot 0.001=44100 \cdot 0.01=441(个采样点) 帧数=fs0.025=441000.025=1103(个采样点)帧移=fs0.001=441000.01=441(个采样点)
\qquad 在这里我们要调用matlab的enframe函数来进行分帧操作,要知道这个函数是包含在voicebox工具箱里边,首先确保其已经安装成功。

S=enframe(y,1103,662);%分帧,对y进行分帧,x为没有预加重的语音序列
%每帧长度为1103个采样点,每帧之间非重叠部分为662个采样点
%1103=44100*0.025,   441=44100*0.01    662=1103-441
%根据计算,我们可以将108721个数据根据公式662*301+1103=200365
%可以将其分为301

3.4 加窗{C(301*1103)}

\qquad 将信号分帧后,我们将每一帧代入窗函数,窗外的值设定为0,其目的是消除各个帧两端可能会造成的信号不连续性(即谱泄露 spectral leakage)。常用的窗函数有方窗、汉明窗和汉宁窗等,根据窗函数的频域特性,常采用汉明窗(hamming window)。接下来我来讲解一下怎么加窗:我们需要做的就是为每一帧数据,也就是301帧数据都加入大小为1103的汉明窗。其汉明窗的表达公式如下所示:
W ( n ) = ( 1 − a ) − a ⋅ c o s ( 2 ⋅ π ⋅ n / N ) 1 ≤ n ≤ N W(n)=(1-a)-a \cdot cos(2\cdot \pi \cdot n/N) \qquad 1 \leq n \leq N W(n)=(1a)acos(2πn/N)1nN
对于a的取值不同,将会产生不同的汉明窗,一般情况下, a = 0.46 a=0.46 a=0.46

%尝试一下汉明窗a=0.46,得到汉明窗W=(1-a)-a*cos(2*pi*n/N)
n=1:1103;
W=0.54-0.46*cos((2*pi.*n)/1103)
plot(W);title('汉明窗');grid on;
xlabel('取样点');ylabel('幅值')
%创建汉明窗矩阵C
C=zeros(301,1103);
for i=1:301C(i,:)=W;
end

\qquad 由上边的公式我们可以得到汉明窗矩阵 C ( 301 , 1103 ) C_{(301,1103)} C(301,1103),其矩阵大小为(301,1103),由于汉明窗矩阵和分帧后的矩阵S具有相同大小,所以在matlab中使这两个矩阵的对应位置相乘,即可得到加窗后的矩阵 S C ( 301 , 1103 ) SC_{(301,1103)} SC(301,1103),其大小为(301,1103)。接下来我将随便选取一帧数据来展示一下汉明窗、原始数据、加窗后的数据。其matlab代码如下所示:

SC=S.*C;
subplot(3,1,1);plot(C(7,:),'r');
title('汉明窗图像');grid on;%画出第7帧的汉明窗图像
subplot(3,1,2);plot(S(7,:),'g');
title('原始信号图像');grid on;%画出第7帧的原始信号图像
subplot(3,1,3);plot(SC(7,:),'m');
title('加了汉明窗的信号图像');grid on;%画出第7帧加了汉明窗的信号图像

在这里插入图片描述
\qquad 在上边的图示中我们就可以看到,在每一帧的低频部分和高频部分都被汉明窗相乘后起了较大抑制作用,使其结果接近于0。

3.5 傅里叶变换

\qquad 对于加窗后的矩阵 S C ( 301 , 1103 ) SC_{(301,1103)} SC(301,1103),它是一个301*1103的矩阵,也就是说,它有301帧数据,且每一帧数据都有1103个采样点,那么我们接下来就要对这301帧的每一帧都要进行N=4096的FFT快速傅里叶变换,得到一个大小为(301,4096)大小的矩阵 D ( 301 , 4096 ) D_{(301,4096)} D(301,4096),其帧数还是301帧,对每一帧的4096个数据点分别取模再取平方,然后除以4096;便得到能量谱密度 E ( 301 , 4096 ) E_{(301,4096)} E(301,4096),其大小为301x4096,然后再对每一帧得到的能量进行相加,即得到一个301x1的矩阵 F ( 301 , 1 ) F_{(301,1)} F(301,1),其中的每个元素代表每一帧能量的总和。

%对SC的每一帧都进行N=4096的快速傅里叶变换,得到一个301*4096的矩阵
F=0;N=4096;
for i=1:301%对SC作N=4096的FFT变换D(i,:)=fft(SC(i,:),N);%以下循环实现求取能量谱密度Efor j=1:Nt=abs(D(i,j));E(i,j)=(t^2)/N;end%获取每一帧的能量总和F(i)F(i)=sum(D(i,:));
end

3.6 梅尔滤波器

\qquad 首先我要讲一下什么是梅尔值,这是一个新的量度,相比于正常的频率机制,梅尔值更加接近于人耳的听觉机制,其在低频范围内增长速度很快,但在高频范围内,梅尔值的增长速度很慢。每一个频率值都对应着一个梅尔值,其对应关系如下
m = 2595 ⋅ l o g 10 ( 1 + f 700 ) m=2595 \cdot log_{10}(1+\frac{f}{700}) m=2595log10(1+700f)
在matlab上画出其对应图像如下所示:
在这里插入图片描述
对于该函数图像的显示的matlab代码如下所示:

%梅尔频率转化函数图像
for i=1:200000mel(i)=2595*log10(1+i/700);
end
set(gcf,'position',[180,160,960,550]);%设置画图的大小
plot(mel,'linewidth',1.5);grid on;
title('梅尔频率转换');xlabel('频率');ylabel('梅尔频率');

但是如果我们要将梅尔频率m转换为频率f呢,我们对上式整理即可得到:
f = 700 ⋅ ( 1 0 m / 2595 − 1 ) f=700 \cdot (10^{m/2595}-1) f=700(10m/25951)
\qquad 好了介绍到这里,对于如何得到梅尔滤波器组我们还是无从下手,于是,我在这里描述了一下获得梅尔滤波器的几个简单步骤。然后接下来的操作我们也就将会按照这个步骤来实现。

在这里插入图片描述
其中过程1、2、3、4的实现代码如下所示:

fl=0;fh=fs/2;%定义频率范围,低频和高频
bl=2595*log10(1+fl/700);%得到梅尔刻度的最小值
bh=2595*log10(1+fh/700);%得到梅尔刻度的最大值
%梅尔坐标范围
p=26;%滤波器个数
B=bh-bl;%梅尔刻度长度
mm=linspace(0,B,p+2);%规划28个不同的梅尔刻度
fm=700*(10.^(mm/2595)-1);%将Mel频率转换为频率

上边几步都比较好理解,但是对于接下来谱线索引号k的定义,或许就需要一些理解了,其定义公式如下所示:
k = ( 1 + N ) ⋅ f m f s k=\frac{(1+N)\cdot f_m}{f_s} k=fs(1+N)fm
\qquad 其中 N N N为FFT点数, f s f_s fs为抽样频率, f m f_m fm为之前那28个梅尔刻度转化为频率后的值,最后我们得到的 k k k值为一个1*28的矩阵。且k值范围为 0 − N / 2 0-N/2 0N/2。这个式子是把频率对应到频谱中2048个频率分量的某个。
以下则是k值得求解过程:

k=((N+1)*fm)/fs%计算28个不同的k值
hm=zeros(26,N);%创建hm矩阵
df=fs/N;
freq=(0:N-1)*df;%采样频率值

好了,现在我们只剩下最后一步了,创建Hm矩阵,这个矩阵得定义如下所示:
H m ( k ) = { 0 k < f ( m − 1 ) 2 ( k − f ( m − 1 ) ) ( f ( m + 1 ) − f ( m − 1 ) ) ( f ( m ) − f ( m − 1 ) ) f ( m − 1 ) ≤ k ≤ f ( m ) 2 ( f ( m + 1 ) − k ) ( f ( m + 1 ) − f ( m − 1 ) ) ( f ( m ) − f ( m − 1 ) ) f ( m ) ≤ k ≤ f ( m + 1 ) 0 k ≥ f ( m + 1 ) H_m(k)=\begin{cases} 0& \text{k}<f(m-1)\\ \frac{2(k-f(m-1))}{(f(m+1)-f(m-1))(f(m)-f(m-1))}& f(m-1)\le k \le f(m)\\ \frac{2(f(m+1)-k)}{(f(m+1)-f(m-1))(f(m)-f(m-1))}& f(m)\le k \le f(m+1)\\ 0& k\ge f(m+1) \end{cases} Hm(k)= 0(f(m+1)f(m1))(f(m)f(m1))2(kf(m1))(f(m+1)f(m1))(f(m)f(m1))2(f(m+1)k)0k<f(m1)f(m1)kf(m)f(m)kf(m+1)kf(m+1)
\qquad 上式中的m代表第几个滤波器,k为横坐标0-2048。26个滤波器就是算2049个频率分量分别属于26个频率带的概率.根据上式计算26个滤波器的二维数组,也就是26*4096二维数组Hm.
以下贴出步骤6 Hm矩阵的求解

for i=2:27%取整,这里取得是28个k中的第2-27个,舍弃028n0=floor(k(i-1));n1=floor(k(i));n2=floor(k(i+1));%要知道k(i)分别代表的是每个梅尔值在新的范围内的映射,其取值范围为:0-N/2%以下实现公式--,求取三角滤波器的频率响应。for j=1:Nif n0<=j & j<=n1hm(i-1,j)=2*(j-n0)/((n2-n0)*(n1-n0));elseif n1<=j & j<=n2hm(i-1,j)=2*(n2-j)/((n2-n0)*(n1-n0));endend%此处求取H1(k)结束。
end

接下来将要进行最后一步,输出Hm矩阵,并且将梅尔滤波器组画出来。

%绘图,且每条颜色显示不一样
c=colormap(lines(26));%定义26条不同颜色的线条
set(gcf,'position',[180,160,1020,550]);%设置画图的大小
for i=1:26plot(freq,hm(i,:),'--','color',c(i,:),'linewidth',2.5);%开始循环绘制每个梅尔滤波器hold on
endgrid on;%显示方格axis([0 1500 0 0.2]);%设置显示范围

\qquad 画出来之后,我们就会发现该梅尔滤波器,在频率很小的时候,滤波器宽度很窄,随着其频率的增大,滤波器的宽度也会逐渐增大,但其幅值也会逐渐减小。
在这里插入图片描述

3.7 离散余弦变换

\qquad 在进行离散余弦变换之前,我们还需要做的就是把第3.5节得到的二维矩阵能量谱 E ( 301 , 4096 ) E_{(301,4096)} E(301,4096),乘以第3.6节得到的二维数组梅尔滤波器 H m ( 26 ∗ 4096 ) Hm_{(26*4096)} Hm(264096)的转置,矩阵的转置可得到301*26的矩阵,然后满足矩阵乘法定律,得到参数H,其定义如下:
H = E ⋅ H m T H=E \cdot H_m^T H=EHmT
此处的H其实是301x26的二维矩阵。
\qquad 由于滤波器组得到的系数是相关性很高的,因此我们用离散余弦变换(Discrete Cosine Transform)来去相关并且降维。一般来说,在自动语音识别(Automatic Speech Recognition)领域,因为大部分信号数据一般集中在变换后的低频区,所以对每一帧只取前13个数据就好了。
好了接下来我们就要进行离散余弦变换了,但是在开始之前我感觉还是先讲解一下其具体的步骤流程吧。
在这里插入图片描述
根据mfcc的定义,我们需要对能量的对数作离散余弦变换,即可得到MFCC参数:
m f c c ( i , n ) = ∑ m = 1 M l o g [ H ( i , m ) ] ⋅ c o s [ π ⋅ n ⋅ ( 2 m − 1 ) 2 M ] mfcc(i,n)=\sum_{m=1}^{M}log[H(i,m)] \cdot cos[\frac{\pi \cdot n \cdot(2m-1)}{2M}] mfcc(i,n)=m=1Mlog[H(i,m)]cos[2Mπn(2m1)]
\qquad 其中H为我们上边得到的矩阵H,M代表梅尔滤波器个数,i代表第几帧数据(取值为1-301),n代表第i帧的第n列(n取值范围为1-26)。那么根据上述公式我们可以写出求取mfcc的代码如下:

H=E*hm';
%对H作自然对数运算%因为人耳听到的声音与信号本身的大小是幂次方关系,所以要求个对数for i=1:301for j=1:26H(i,j)=log(H(i,j));%取对数运算endend%作离散余弦变换   for i=1:301for j=1:26%先求取每一帧的能量总和sum=0;%作离散余弦变换for p=1:26sum=sum+H(i,p)*cos((pi*j)*(2*p-1)/(2*26));endmfcc(i,j)=((2/26)^0.5)*sum;  %完成离散余弦变换end    end

\qquad 接下来我们就要根据公式进行升倒谱的计算了,前边我们已经讲到了,因为大部分的信号数据一般集中在变换后的低频区,所以对每一帧只取前13个数据就好了。其公式表达如下:
K ( i ) = 1 + ( L 2 ) ⋅ s i n ( π ⋅ i L ) i = 1 , 2 , 3... , 13 K(i)=1+(\frac{L}{2})\cdot sin(\frac{\pi \cdot i}{L}) \qquad i=1,2,3...,13 K(i)=1+(2L)sin(Lπi)i=1,2,3...,13
\qquad 其中L为升倒谱系数,一般取值为22,我们将其带入即可求得矩阵K,这是一个1x13大小的矩阵,这一部分的升倒谱的其实现代码如下:

J=mfcc(:,(1:13));
%默认升到普系数为22for i=1:13K(i)=1+(22/2)*sin(pi*i/22);end

接下来我们就要求取MFCC的三个参数了,首先我们先获取mfcc的第一组数据,根据公式:
f e a t ( i , j ) = J ( i , j ) ⋅ K ( j ) i = 1 , 2 , 3 , . . . , 301 ; j = 1 , 2 , . . . , 13 feat(i,j)=J(i,j) \cdot K(j)\\ \quad \\ \quad i=1,2,3,...,301; \quad j=1,2,...,13 feat(i,j)=J(i,j)K(j)i=1,2,3,...,301;j=1,2,...,13
根据公式我们可以实现如下代码:

 %得到二维数组feat,这是mfcc的第一组数据,默认为三组for i=1:301for j=1:13L(i,j)=J(i,j)*K(j);endendfeat=L;%将其值赋值到feat矩阵

\qquad 接下来就是求取MFCC的第二组,第三组参数了。第二组参数其实就是在已有的基础参数下作一阶微分操作,第三组参数在第二组参数下作一阶微分操作。那么表达公式事什么样的呢,别急,等我慢慢道来:
d t f e a t [ i ] [ j ] = f e a t [ i + 1 ] [ j ] − f e a t [ i − 1 ] [ j ] + 2 ⋅ f e a t [ i + 2 ] [ j ] − 2 ⋅ f e a t [ i − 2 ] [ j ] i = 1 , 2 , 3 , . . . , 301 ; j = 1 , 2 , 3... , 13 dtfeat[i][j]=feat[i+1][j]-feat[i-1][j]+2\cdot feat[i+2][j]-2 \cdot feat[i-2][j] \\ \quad \\ i=1,2,3,...,301 \quad; \quad j=1,2,3...,13 dtfeat[i][j]=feat[i+1][j]feat[i1][j]+2feat[i+2][j]2feat[i2][j]i=123...,301;j=1,2,3...,13
按照上边的公式,我们可以使用代码实现一阶求导和二阶求导的计算

%接下来求取第二组(一阶差分系数)301x13 ,这也是mfcc参数的第二组参数dtfeat=0;dtfeat=zeros(size(L));%默认初始化for i=3:299dtfeat(i,:)=-2*feat(i-2,:)-feat(i-1,:)+feat(i+1,:)+2*feat(i+2,:); enddtfeat=dtfeat/10;
%求取二阶差分系数,mfcc参数的第三组参数
%二阶差分系数就是对前面产生的一阶差分系数dtfeat再次进行操作。dttfeat=0;dttfeat=zeros(size(dtfeat));%默认初始化for i=3:299dttfeat(i,:)=-2*dtfeat(i-2,:)-dtfeat(i-1,:)+dtfeat(i+1,:)+2*dtfeat(i+2,:); enddttfeat=dttfeat/10;%这里的10是根据数据确定的

\qquad 好了到这里我们就完成了,MFCC三组参数的求解,我们现在就只需要将这三组数据拼接到一起形成一个301x39的矩阵即可。其实现代码如下:

%将得到的mfcc的三个参数feat、dtfeat、dttfeat拼接到一起%得到最后的mfcc系数301x39mfcc_final=0;mfcc_final=[feat,dtfeat,dttfeat];%拼接完成

\qquad 前面导图中,我们也有讲到过,由于一阶求导和二阶求导的前两帧和后两帧数据都为0,于是我们就要对在mfcc_final中去除这四帧数据。而后我们再选取每一帧的mfcc系数的第一个数得到 M F C C 0 MFCC_0 MFCC0,这是一个297x1的数据,对 M F C C 0 MFCC_0 MFCC0来进行绘图,并与原始信号进行比对。

mfcc24=mfcc_final((3:299),:);%以下画图对比一下原始信号和提取出来的mfcc0set(gcf,'position',[180,160,960,550]);%设置画图的大小subplot(211)plot(x,'m');grid on;title('原始信号');axis([0 200000 -1 1]);%对数据,进行绘图mfcc0=mfcc24(:,1)%选取mfcc系数的第一个数,组成新的特征参数mfcc0subplot(212)mfcc00=(mfcc0-80)/2		%PS:这里不在mfcc求取过程步骤中,我只是为方便作图而修改的这里,希望大家可以理解。MFCC参数的求取过程并无此操作plot(mfcc00,'r','linewidth',2);title('MFCC_0');axis([0 300 -30 5]);grid  on

比对结果如下所示:
在这里插入图片描述
\qquad 好了,到了这里我们就可以看到了,原始信号之前是20000个采样点的数据,而现在的 M F C C 0 MFCC_0 MFCC0参数图形大致与原始信号一致,并且其点数只有297个点,这也就说明通过此方法 M F C C 0 MFCC_0 MFCC0,我们可以提取出语音信号的特点以及走向趋势,也就是说在某个程度上我们可以用这297个点来代替 2 ⋅ 1 0 5 2 \cdot 10^5 2105点的数据。

3.8 注(2022年5月3日修)

上边图示 M F C C 0 MFCC_0 MFCC0仅为语音MFCC参数的第一维参数,因其( M F C C 0 MFCC_0 MFCC0)包含了语音信号的时域能量信息,所以一般被用来用作语音信号的 端点检测。另外还有很多同学对变量的理解不是那么明白,这里特此说明一下:

实验中变量描述
featMFCC参数
dtfeatMFCC参数的一阶差分
dttfeatMFCC参数的二阶差分
mfcc_finalMFCC参数+一阶差分+二阶差分
M F C C 0 MFCC_0 MFCC0MFCC参数的第一维参数

最后还是要说明一下,此篇博客只是为了让大家深入理解MFCC特征的求取过程,所以其在 运行速度代码规范性 方面未作过多优化,如果大家是用于做实验研究,建议大家可以直接调用matlab自带的mfcc函数(matlab2019版以后已经自带mfcc的函数),

[x,fs]=audioread(filename);%读取wav文件
wlen=512;%帧长
inc=256;%帧移
dim=13;%MFCC参数维度
[mfccs,delta_mfcc,delta_delta_mfcc]= mfcc(x,fs,'WindowLength',wlen,'OverlapLength',inc ,'NumCoeffs',dim,'LogEnergy','Ignore'); 
%%这里mfccs表示13维的mfcc参数,delta_mfcc表示其一阶差分,delta_delta_mfcc表示其二阶差分

那如果你的matlab版本较低,则可以参考4.1节代码。

4.总结

本次训练是在参考了很多资料的前提下完成的,为了防止自己忘记,所以特此写了本篇文章。

4.1更新(2021.5.9)

很多同学想要这个mfcc版本的完整版,再加之上边的讲解是只针对自己语音,一些语音长度,包括语音帧数使得部分同学难以理解,所以在这里我又将程序进行了一下修改,使同学们可以自己输入自己的音频文件,并返回mfccs参数矩阵:

function [mfcc_final]=Mymfcc(filename)frameSize=512;
inc=160;
[x,fs]=audioread(filename);%读取wav文件
N=length(x);
%预加重y=x(i)-0.97*x(i-1)
for i=2:Ny(i)=x(i)-0.97*x(i-1);
end
y=y';%对y取转置
S=enframe(y,frameSize,inc);%分帧,对x进行分帧,
[a b]=size(S);
%尝试一下汉明窗a=0.46,得到汉明窗W=(1-a)-a*cos(2*pi*n/N)
n=1:b;
W=0.54-0.46*cos((2*pi.*n)/b);
%创建汉明窗矩阵C
C=zeros(a,b);
ham=hamming(b);
for i=1:aC(i,:)=ham;
end
%将汉明窗C和S相乘得SC
SC=S.*C;
F=0;N=4096;
for i=1:a%对SC作N=4096的FFT变换D(i,:)=fft(SC(i,:),N);%以下循环实现求取能量谱密度Efor j=1:Nt=abs(D(i,j));E(i,j)=(t^2)/N;end%获取每一帧的能量总和F(i)F(i)=sum(D(i,:));
end%将频率转换为梅尔频率
%梅尔频率转化函数图像
N1=length(x)
for i=1:N1mel(i)=2595*log10(1+i/700);
endfl=0;fh=fs/2;%定义频率范围,低频和高频
bl=2595*log10(1+fl/700);%得到梅尔刻度的最小值
bh=2595*log10(1+fh/700);%得到梅尔刻度的最大值
%梅尔坐标范围
p=26;%滤波器个数
B=bh-bl;%梅尔刻度长度
mm=linspace(0,B,p+2);%规划28个不同的梅尔刻度
fm=700*(10.^(mm/2595)-1);%将Mel频率转换为频率
W2=N/2+1;%fs/2内对应的FFT点数,2049个频率分量k=((N+1)*fm)/fs%计算28个不同的k值
hm=zeros(26,N);%创建hm矩阵
df=fs/N;
freq=(0:N-1)*df;%采样频率值%绘制梅尔滤波器
for i=2:27%取整,这里取得是28个k中的第2-27个,舍弃0和28n0=floor(k(i-1));n1=floor(k(i));n2=floor(k(i+1));%要知道k(i)分别代表的是每个梅尔值在新的范围内的映射,其取值范围为:0-N/2%以下实现公式--,求取三角滤波器的频率响应。for j=1:Nif n0<=j & j<=n1hm(i-1,j)=2*(j-n0)/((n2-n0)*(n1-n0));elseif n1<=j & j<=n2hm(i-1,j)=2*(n2-j)/((n2-n0)*(n1-n0));endend%此处求取H1(k)结束。
end
%绘图,且每条颜色显示不一样
c=colormap(lines(26));%定义26条不同颜色的线条
figure(1)
set(gcf,'position',[180,160,1020,550]);%设置画图的大小
for i=1:26plot(freq,hm(i,:),'--','color',c(i,:),'linewidth',2.5);%开始循环绘制每个梅尔滤波器hold on
endgrid on;%显示方格axis([0 1500 0 0.1]);%设置显示范围%得到能量特征参数的和H=E*hm';%对H作自然对数运算%因为人耳听到的声音与信号本身的大小是幂次方关系,所以要求个对数for i=1:afor j=1:26H(i,j)=log(H(i,j));endend%作离散余弦变换   具体参考信号检测与估计的论文for i=1:afor j=1:26%先求取每一帧的能量总和sum1=0;%作离散余弦变换for p=1:26sum1=sum1+H(i,p)*cos((pi*j)*(2*p-1)/(2*26));endmfcc(i,j)=((2/26)^0.5)*sum1;  %完成离散余弦变换end    end%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%   以下为求取mfcc的三个参数过程  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%作升倒谱运算%因为大部分的信号数据一般集中在变换后的低频区,所以对每一帧只取前13个数据就好了J=mfcc(:,(1:13));%默认升到普系数为22for i=1:13K(i)=1+(22/2)*sin(pi*i/22);end%得到二维数组feat,这是mfcc的第一组数据,默认为三组for i=1:afor j=1:13L(i,j)=J(i,j)*K(j);endendfeat=L;%接下来求取第二组(一阶差分系数)301x13 ,这也是mfcc参数的第二组参数dtfeat=0;dtfeat=zeros(size(L));%默认初始化for i=3:a-2dtfeat(i,:)=-2*feat(i-2,:)-feat(i-1,:)+feat(i+1,:)+2*feat(i+2,:); enddtfeat=dtfeat/10;%这里的/10,是我自己在实现过程中的操作,原mfcc的差分求取并无/10操作(2022年5月3日注)
%求取二阶差分系数,mfcc参数的第三组参数
%二阶差分系数就是对前面产生的一阶差分系数dtfeat再次进行操作。dttfeat=0;dttfeat=zeros(size(dtfeat));%默认初始化for i=3:a-2dttfeat(i,:)=-2*dtfeat(i-2,:)-dtfeat(i-1,:)+dtfeat(i+1,:)+2*dtfeat(i+2,:); enddttfeat=dttfeat/10;%同上dtfeat%这里的10是根据数据确定的,默认为2%将得到的mfcc的三个参数feat、dtfeat、dttfeat拼接到一起%得到最后的mfcc系数301x39mfcc_final=0;mfcc_final=[feat,dtfeat,dttfeat];%拼接完成

上边封装的函数是我根据上边分步讲解的内容,封装为Mymfcc函数,调用方法也很简单:

mfccs=Mymfcc('bluesky1.wav')

这里返回的参数就是一个197*39的mfcc参数矩阵,当然这个矩阵的大小还是由输入的语音长度决定的。

4.2.实验中diguashao.wav自取(2022年5月3日更新)

实验过程中,有些同学如果想要实验过程中的 diguashao.wav,我已经将该文件上传至百度网盘地址链接,提取码为:fjq6
希望可以帮助到大家。

5.参考文献,资料

1.基于谱熵梅尔积的语音端点检测方法
2.语音识别MFCC
3.语音特征参数MFCC提取过程详解
4.Mel滤波器组的设计与实现(基于MATLAB和Python)


http://chatgpt.dhexx.cn/article/9Iu29eCB.shtml

相关文章

Parquet encoding

Dictionary encoding

Parquet原理剖析

行存VS列存 广义的数据分析系统大致分为可以分为计算层、数据格式层和存储层。 计算层主要负责数据查询的介入和各种逻辑计算&#xff0c;如&#xff1a;MR、Spark、Flink。 存储层承载数据持久化存储&#xff0c;以文件语义或类似文件语义(对象存储)对接计算层。 数据格式层&…

Spark 实战 - 3.一文搞懂 parquet

一.引用 parquet 文件常见于 Spark、Hive、Streamin、MapReduce 等大数据场景&#xff0c;通过列式存储和元数据存储的方式实现了高效的数据存储与检索&#xff0c;下面主要讲 parquet 文件在 spark 场景下的存储&#xff0c;读取与使用中可能遇到的坑。 二.Parquet 加载方式 …

Spark Parquet使用

Spark SQL下的Parquet使用最佳实践和代码实战 分类&#xff1a; spark-sql&#xff08;1&#xff09; 一、Spark SQL下的Parquet使用最佳实践 1&#xff09;过去整个业界对大数据的分析的技术栈的Pipeline一般分为以下两种方式&#xff1a; a&#xff09;Data Source -> HD…

Arrow 之 Parquet

Parquet-format 左边是文件开头及具体的数据&#xff0c; 右边是文件结尾的 Footer Metadata There are three types of metadata: file metadata, column (chunk) metadata and page header metadata. All thrift structures are serialized using the TCompactProtocol. Co…

parquet存入mysql_解密列存 parquet

在做数据分析的时候,相对于传统关系型数据库,我们更倾向于计算列之间的关系。在使用传统关系型数据库时,基于此的设计,我们会扫描很多我们并不关心的列,这导致了查询效率的低下,大部分数据库 io 比较低效。因此目前出现了列式存储。Apache Parquet 是一个列式存储的文件格…

Parquet原理

在互联网大数据应用场景下&#xff0c;通常数据量很大且字段很多&#xff0c; 但每次查询数据只针对其中的少数几个字段&#xff0c;这时候列式存储是极佳的选择。 列式存储要解决的问题&#xff1a; 把IO只给查询需要用到的数据 只加载需要被计算的列空间节省 列式的压缩效…

parquet--golang使用

github 其实如果不适用一些可视化工具解析parquet文件&#xff0c;不太好看parquet文件内部正常应该是什么样的。但是使用一些可视化工具的话&#xff0c;可以发现&#xff0c;parquet文件会像表格&#xff0c;如excel文件&#xff0c;csv文件那样&#xff0c;排列数据。通过结…

Parquet

动机 创建Parquet是利用压缩性,高效的列式存储来在Haddop生态圈任何项目中应用. 记住Parquet是构建在复杂嵌套的数据结构, 并且使用记录分解和集成的算法在Dremely论文中描述.我们相信这种方法是更强大的的可以非常简单的使嵌套命令空间的扁平化. Parquet构建可以非常高效的…

Parquet 存储格式

1.介绍 Apache Parquet 是 Hadoop 生态圈中一种新型列式存储格式&#xff0c;它可以兼容 Hadoop 生态圈中大多数计算框架(Mapreduce、Spark 等)&#xff0c;被多种查询引擎支持&#xff08;Hive、Impala、Drill 等&#xff09;&#xff0c;并且它是语言和平台无关的。 2.特点…

parquet 简介

参考文章&#xff1a;parquet 简介 Parquet原理 【2019-05-29】Parquet 简介 Apache Parquet是一种能够有效存储嵌套数据的列式存储格式。 面向分析型业务的列式存储格式 由 Twitter 和 Cloudera 合作开发&#xff0c;2015 年 5 月从 Apache 的孵化器里毕业成为 Apache 顶…

Parquet文件详解

1、parquet文件简介 Apache Parquet是Apache Hadoop生态系统的一种免费的开源面向列的数据存储格式。 它类似于Hadoop中可用的其他列存储文件格式&#xff0c;如RCFile格式和ORC格式。 Apache Parquet 是由 Twitter 和 Cloudera 最先发起并合作开发的列存项目&#xff0c;也是…

Gson解析json数据

gson是谷歌推出的&#xff0c;除此之外还有阿里的FastJson&#xff0c;官方json和jackjson。下面通过一个实例来讲解使用gson来解析json数据&#xff1a; 1.先做好准备工作&#xff0c;在网上下载Gson的jar包&#xff0c;放到工程的libs(没有此目录的话自己建一个)目录下: ht…

Android Gson解析json

前言&#xff1a; 解析json的库有很多&#xff0c;如&#xff1a;JSON-Java、Gson、Jackson、FastJson…而Gson是谷歌的&#xff0c;相信自有它的好处 简介 用于json与java对象之间的转换通过 序列化和反序列化 实现功能强大&#xff0c;稳定性也好 使用 Gson提供了两个方…

Android 使用 Gson 解析 json 数据及生成

1.导入 Gson 包 第一种导入Gson 包的方式 在 app 文件下的 build.gradle 文件 导入 gson:2.9.1 包 implementation com.google.code.gson:gson:2.9.1第二种导入Gson 包的方式 直接去下载最新的 Gson 包 下载链接&#xff1a;gson.jar 选择最新的包进行下载 将下载的 gson…

用Gson解析json

首先我们需要导入gson的jar包&#xff0c;因为gson解析方法不是java官方的而是谷歌提供的。 一.把json数据转成java对象 首先因为已经手动导入了jar包&#xff0c;现在只需创建解析器对象&#xff0c;当然首先得有一个json类型的文件地址&#xff0c;和文件输出流 第二步调用…

Android --Gson解析json数据

Android --Gson解析json数据 private void analyseJson() throws Exception {InputStream isgetAssets().open("dataTest.json");ByteArrayOutputStream baosnew ByteArrayOutputStream();byte[] bytesnew byte[1024];int len;while ((lenis.read(bytes))!-1){baos.…

Gson解析json字符串

Gson 怎样使用gson把一个json字符串解析成一个jsonObject对象 因此我要把上面的fastjson转换成是gson&#xff0c;如下图&#xff1a; JsonObject object new JsonParser().parse(result).getAsJsonObject();怎样从gson中取出键的值 使用gson把json字符串转换成一个list集合 …

使用Gson解析Json数据

目录 一、Gson介绍 二、使用方法 完整代码&#xff1a; MainActivity: 布局&#xff1a; 运行结果&#xff1a; 一、Gson介绍 Gson是Google提供的一个Java库&#xff0c;用于将Java对象转换为JSON格式数据或将JSON格式数据转换为Java对象。 常用方法&#xff1a; 方法名…

用GSON解析Json格式数据

GSON是谷歌提供的开源库&#xff0c;用来解析Json格式的数据&#xff0c;非常好用。如果要使用GSON的话&#xff0c;则要先下载gson-2.2.4.jar这个文件&#xff0c;如果是在Android项目中使用&#xff0c;则在Android项目的libs目录下添加这个文件即可&#xff1b;如果是在Java…