前言
最近学校的大作业,以为还要编写实现MFCC系数的提取程序(包括分帧、加窗、帧移、DCT等操作),结果上csdn一查有现成的函数直接出结果。本文不会详细讲原理,也不讨论识别正确率(因为自己也没测试对比过),就只是贴点程序讨论一下,顺便作为自己的研究笔记,如有错误,欢迎指正。
音乐特征库的建立
本文从整个音乐特征匹配与识别的流程来讲述。
批量导入音乐文件
music_bank=dir(fullfile('D:\CloudMusic','*.mp3'));
music_names={music_bank.name}';
music_names=strrep(music_names,'.mp3',''); %名字列表中去掉后缀名.mp3
for i=1:length(music_bank)
filename=strcat('D:\CloudMusic\',music_names{i},'.mp3');
[x,fs]=audioread(filename);
x=x((fs*5+1:fs*15),:); %特征提取库不需要太长时间段可自选,最好不要从0秒开始,有些音乐前奏还没开始
%(未结束)
音乐库均保存在某一文件夹下,“*.mp3”后缀,由于作为特征库,其导入的数据作为元胞类型,反正之后无需修改。获取的music_names去掉歌名的后缀.mp3为了后续在显示听歌识曲结果时查找原歌名的时候需要(这里应该有别的方式)。
之后进入循环,循环audioread读入[x,fs],进行MFCC系数提取。
mfcc_m、mfcc、v_melcepst分析比较
看了csdn上很多关于mfcc的matlab实现,大多都是引用自宋知用老师编著的《MATLAB在语音信号分析与合成中的应用》中的程序,这里给出一篇博客的链接戳这里(只是我看到的引用过此程序的博客之一),注释还算详细。
首先从来源于此文的mfcc_m函数讲起,我修改的代码先贴出来(注意注释):
function ccc=mfcc_m(x,fs,p,L,framesize,inc)
% function ccc=mfcc_m(x);
%对输入的语音序列x进行MFCC参数的提取,
%返回MFCC参数和一阶、二阶(可选)差分MFCC参数,Mel滤波器的个数为p,
%MFCC参数个数L,采样频率为fs
%原文参数个数L确定,我觉得可以改成自定
%按帧长为n点分为一帧,相邻两帧之间的帧移为inc
bank=v_melbankm(p,framesize,fs,0,0.5,'t');
%归一化Mel滤波器组系数
bank=full(bank); %将稀疏矩阵转换为满存储
bank=bank/max(bank(:));
%计算DCT系数 L*p
for k=1:L %MFCC参数的个数,通常取12个,一般在12-16个之间
n=0:p-1;
dctcoef(k,:)=cos((2*n+1)*k*pi/(2*p));
end
%归一化倒谱提升窗口
%ceplifter = @( N, L )( 1+0.5*L*sin(pi*[0:N-1]/L) );
w=1+0.5*L*sin(pi*(1:L)/L); %c'=(1+0.5*L*sin(pi*n/L))cn 倒谱提升
w=w/max(w);
%预加重滤波器
xx=double(x);
xx=filter([1,-0.93],1,xx); % 通过一个HF,H(z)=1-uz^(-1),u可看效果自取
%语音信号分帧
xx=v_enframe(xx,framesize,inc);
n2=fix(framesize/2)+1;
%计算每帧的MFCC参数
for i=1:size(xx,1)
y=xx(i,:);
s=y'.*hamming(framesize);
t=abs(fft(s));
t=t.^2;
c1=dctcoef*log(bank*t(1:n2)); %由于fft对称性,当N为偶数时取N/2+1个点
c2=c1.*w';
c2=c2.*sqrt(2.0/p); % 一维DCT公式(原文中没有这个系数,但是查询了DCT公式却有这个系数)
m(i,:)=c2';
end
%求一阶差分系数
dtm=zeros(size(m));
for i=3:size(m,1)-2
dtm(i,:)=-2*m(i-2,:)-m(i-1,:)+m(i+1,:)+2*m(i+2,:);
end
dtm=dtm/3;
%求取二阶差分系数
dtmm=zeros(size(dtm));
for i=3:size(dtm,1)-2
dtmm(i,:)=-2*dtm(i-2,:)-dtm(i-1,:)+dtm(i+1,:)+2*dtm(i+2,:);
end
dtmm=dtmm/3;
%合并mfcc参数和一阶、二阶差分mfcc参数
ccc=[m dtm dtmm];
%去除首尾两帧,因为这两帧的一阶差分参数为0
ccc=ccc(3:size(m,1)-2,:);
其中,有v_melbankm函数,可以翻看我之前的一篇博客有详细解释这个函数的用法及参数含义戳这里。
归一化倒谱提升窗口我看宋老师也没讲明白,全网我也没看到提及这个的博客,我目前也没找到相关资料。总的来说,mfcc_m还是借助v_melbankm函数提取MFCC系数的,在预处理和选取MFCC参数种类上做了处理。
mfcc 是Matlab自带的函数,源代码用open mfcc
命令看,以下简略讲讲自己对该函数的理解。
[coeffs,delta,deltaDelta,loc] = mfcc(x,fs,
'WindowLength',WINDOWLENGTH, %(帧长 default=round(0.030*fs))
'OverlapLength',OVERLAPLENGTH, %(帧移 default=round(fs*0.02))
'NumCoeffs',NUMCOEFFS, %(参数个数 default=13)
'FFTLength',FFTLENGTH, %(FFT长度,被帧长限制,一般不需要提及)
'DeltaWindowLength',DELTAWINDOWLENGTH, %(一阶、二阶的窗长,默认为2)
'LogEnergy',LOGENERGY); %(关于log能量使用与否等)
%LOGENERGY='Append'(default) or 'Replace' or 'Ignore'
%loc是用来计算系数的最新样本
其中,coeffs为一个L*M*N矩阵:
L为音频信号被分割成的帧数,这是由窗口长度和重叠长度属性决定的。
M为每帧返回的系数,这是由NUMCOEFFS属性决定的。
N为通道数(可以分析双声道)。
LogEnergy下的选项解释:
‘Append’——LogEnergy(第1列)+Num列参数
注意:0号系数(?)(zeroth coefficient)在第2列
‘Replace’——Num列(第一列log-energy替换0)
‘Ignore’——忽略并且不返回log-energy
之后,ccc=[coeffs delta dletaDelta]
可生成如mfcc_m返回的参数数据形式,注意提取单声道即可。
v_melcepst 源代码中调用了v_melbankm函数,输入参数内容和mfcc_m差不多,关于此函数详细介绍戳这里。
笔者做实验对比了一下:
v_melcepst(x,fs,'E0dD',13,40)=mfcc(x,fs,'WindowLength',512,'OverlapLength',256,'NumCoeffs',14,'LogEnergy','Append')
(可以看出v_melcepst 默认帧长512,帧移256)
二者得出的MFCC系数维度相同,第一列为Log-Energy,第二列为zeroth coeffcients,但是数值不一样。
因此,在循环中使用上述方式即可提取MFCC参数:
%方案自选
%ccc{i}=v_melcepst(x,fs,'E0dD',13,40);
%[coeffs,delta,deltaDelta,~]=mfcc(x,fs,'WindowLength',512,'OverlapLength',256,'NumCoeffs',14,'LogEnergy','Append');
%ccc{i}=[coeffs(:,:,1) delta(:,:,1) deltaDelta(:,:,1)];
end %循环结束
save('music_base.mat','ccc','music_names');
最后把元胞数据保存成.mat,供识别部分使用此特征库。
音乐识别
测试音频剪切
load('music_base.mat');
for i=1:length(music_names)
filename=strcat('D:\CloudMusic\',music_names{i},'.mp3');
[x,fs]=audioread(filename);
x=x((fs*7+1:fs*(7+10)),:);%tstart、tend
out_names{i}=strcat('C:\Users\Dell\Desktop\test_music\','test',music_names{i},'.wav');
audiowrite(out_names{i},x,fs);
end
批量剪切容易,可以根据需要剪切不同时间段的10秒片段。
特征匹配
为了做批量测试,同样根据length(music_names)
循环:
for i=1:length(music_names)
test_names{i}=strcat('C:\Users\Dell\Desktop\test_music\','test',music_names{i},'.wav');
test_names=test_names';
[x,fs]=audioread(test_names{i});
%测试样本的mfcc提取方法可以做测试,比较不同方案的正确率
%ctest=v_melcepst(x,fs,'E0dD',14,40);
%[coeffs,delta,deltaDelta,loc]=mfcc(x,fs,'WindowLength',512,'OverlapLength',256,'NumCoeffs',14,'LogEnergy','Append');
%ctest=[coeffs(:,:,1) delta(:,:,1) deltaDelta(:,:,1)];
% dtw, matlab自带这个函数
for j=1:length(ccc)
dist{j}=dtw(ctest,ccc{j});
end
% 查找结果
result=dist{1};
for j=1:length(ccc)
if result>dist{j}
result=dist{j};
end
index=find([dist{:}]==result);
end
% 显示查找结果
fprintf('test %d:The music is %s\n',i,music_names{index});
end
目前这几个部分的实现感觉还有些散,争取把功能做成函数包装一下更简单。
之后会做做GUI吧。