├── .gitattributes ├── .gitignore ├── README.md ├── WaveFunction.cpp ├── WaveFunction.h ├── WaveStructure.h └── main.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MFCC-DTW 2 | # Simple MFCC extractor and an speech recognition algorithm (Dynamic Time Warping) 3 | 4 | 5 | # 一个MFCC参数提取模板,和语音识别算法(DTW) 6 | 此模板采用25个滤波器 7 | 8 | # main.cpp里有使用方法 9 | 10 | 11 | -------------------------------------------------------------------------------- /WaveFunction.cpp: -------------------------------------------------------------------------------- 1 | #include "WaveFunction.h" 2 | 3 | WaveFunction::WaveFunction(int frm_len, int mfcc_num){ 4 | MFCC_P=mfcc_num; 5 | MFCC_Pf=float(mfcc_num); 6 | FrmLen=frm_len; 7 | FFTLen=frm_len; 8 | Hamming=new double[FrmLen]; 9 | } 10 | 11 | WaveFunction::~WaveFunction(){ 12 | delete []Hamming; 13 | } 14 | 15 | vector > WaveFunction::getMFCCs(string filename){ 16 | xishu.clear(); 17 | SourceMFCCs.clear(); 18 | //mfcc分析 19 | mTWavHeader waveheader; 20 | FILE *sourcefile; 21 | short buffer[FrmLen]; 22 | float data[FrmLen]; 23 | float FiltCoe1[FFTLen/2+1]; //左系数 24 | float FiltCoe2[FFTLen/2+1]; //右系数 25 | int Num[FFTLen/2+1]; //决定每个点属于哪一个滤波器 26 | float En[FiltNum+1]; //频带能量 27 | vector > vecList; 28 | 29 | sourcefile=fopen(filename.c_str(),"rb"); 30 | fread(&waveheader,sizeof(mTWavHeader),1,sourcefile); 31 | InitHamming();//初始化汉明窗 32 | InitFilt(FiltCoe1,FiltCoe2,Num); //初始化MEL滤波系数 33 | 34 | while(fread(buffer,sizeof(short),FrmLen,sourcefile)==FrmLen){ 35 | HammingWindow(buffer,data); 36 | ComputeFFT(data,vecList); 37 | Filt(data, FiltCoe1, FiltCoe2, Num, En,vecList); 38 | MFCC(En); 39 | vecList.clear(); 40 | fseek(sourcefile, -FrmLen/2, SEEK_CUR);//考虑到帧移,每次移动半帧 41 | } 42 | 43 | int stdlength=xishu.size(); 44 | 45 | for(int i=0;i temp; 47 | for(int j=0;j > WaveFunction::addFirstOrderDifference(vector > mfccs){ 56 | vector > temp; 57 | for(int i=0;i line=mfccs[i]; 59 | int size=line.size(); 60 | for(int t=0;tsize-2||t==size-2) 65 | line.push_back(line[t]-line[t-1]); 66 | else{ 67 | float fenzi=line[t+1]-line[t-1]+2*(line[t+2]-line[t-2]); 68 | float fenmu=sqrtf(10); 69 | line.push_back(fenzi/fenmu); 70 | 71 | } 72 | } 73 | } 74 | temp.push_back(line); 75 | } 76 | return temp; 77 | } 78 | 79 | vector > WaveFunction::addOrderDifference(vector > mfccs){ 80 | vector > temp; 81 | for(int i=0;i line=mfccs[i]; 83 | int size=line.size(); 84 | //一阶差分 85 | for(int t=0;tsize-2||t==size-2) 90 | line.push_back(line[t]-line[t-1]); 91 | else{ 92 | float fenzi=line[t+1]-line[t-1]+2*(line[t+2]-line[t-2]); 93 | float fenmu=sqrtf(10); 94 | line.push_back(fenzi/fenmu); 95 | 96 | } 97 | } 98 | } 99 | //二阶差分 100 | for(int t=size;tsize-2||t==size-2) 105 | line.push_back(line[t]-line[t-1]); 106 | else{ 107 | float fenzi=line[t+1]-line[t-1]+2*(line[t+2]-line[t-2]); 108 | float fenmu=sqrtf(10); 109 | line.push_back(fenzi/fenmu); 110 | 111 | } 112 | } 113 | } 114 | temp.push_back(line); 115 | } 116 | return temp; 117 | } 118 | 119 | float WaveFunction::ComputeDTW(vector > cep1, vector > cep2) 120 | { 121 | vector temp; 122 | for(int i=0;i temp1; 130 | for(int i=0;i=len) 144 | return 0; 145 | else if(first[i-1]>second[i-1]) 146 | return 1; 147 | else 148 | return -1; 149 | } 150 | 151 | void WaveFunction::InitHamming(){ 152 | 153 | float twopi; 154 | int i; 155 | twopi=8.0F*atan(1.0F); 156 | for(i=0;i= (float)FiltFreq[j] && Freq <= (float)FiltFreq[j+1]) 180 | { 181 | Num[i] = j; 182 | if(j == 0) 183 | { 184 | FiltCoe1[i] = 0.0F; 185 | } 186 | else 187 | { 188 | FiltCoe1[i] = ((float)(FiltFreq[j]+BW[j])-Freq) / (float)(BW[j]); 189 | } 190 | FiltCoe2[i] = (Freq-(float)(FiltFreq[j+1]-BW[j+1])) / (float)(BW[j+1]); 191 | FiltCoe1[i] = FiltCoe1[i] * FiltCoe1[i]; 192 | FiltCoe2[i] = FiltCoe2[i] * FiltCoe2[i]; 193 | break; 194 | } 195 | } 196 | } 197 | 198 | } 199 | 200 | void WaveFunction::HammingWindow(short *buf, float *data){ 201 | int i; 202 | for(i=0;i > &vecList){ 207 | for(int i=0;i temp(data[i]); 212 | vecList.push_back(temp); 213 | } 214 | else 215 | { 216 | complex temp(0); 217 | vecList.push_back(temp); 218 | } 219 | } 220 | FFT(FFTLen,vecList); 221 | } 222 | 223 | void WaveFunction::FFT(const unsigned long &ulN, vector > &vecList){ 224 | //得到幂数 225 | 226 | unsigned long ulPower = 0; //幂数 227 | unsigned long ulN1 = ulN - 1; 228 | while(ulN1 > 0) 229 | { 230 | ulPower++; 231 | ulN1 /= 2; 232 | } 233 | //反序 234 | 235 | bitset bsIndex; //二进制容器 236 | unsigned long ulIndex; //反转后的序号 237 | unsigned long ulK; 238 | for(unsigned long p = 0; p < ulN; p++) 239 | { 240 | ulIndex = 0; 241 | ulK = 1; 242 | bsIndex = bitset(p); 243 | for(unsigned long j = 0; j < ulPower; j++) 244 | { 245 | ulIndex += bsIndex.test(ulPower - j - 1) ? ulK : 0; 246 | ulK *= 2; 247 | } 248 | 249 | if(ulIndex > p) 250 | { 251 | complex c = vecList[p]; 252 | vecList[p] = vecList[ulIndex]; 253 | vecList[ulIndex] = c; 254 | } 255 | } 256 | 257 | //计算旋转因子 258 | 259 | vector > vecW; 260 | for(unsigned long i = 0; i < ulN / 2; i++) 261 | { 262 | vecW.push_back(complex(cos(2 * i * PI / ulN) , -1 * sin(2 * i * PI / ulN))); 263 | } 264 | 265 | /*for(unsigned long m = 0; m < ulN / 2; m++) 266 | { 267 | cout<< "\nvW[" << m << "]=" << vecW[m]; 268 | } */ 269 | 270 | //计算FFT 271 | 272 | unsigned long ulGroupLength = 1; //段的长度 273 | unsigned long ulHalfLength = 0; //段长度的一半 274 | unsigned long ulGroupCount = 0; //段的数量 275 | complex cw; //WH(x) 276 | complex c1; //G(x) + WH(x) 277 | complex c2; //G(x) - WH(x) 278 | for(unsigned long b = 0; b < ulPower; b++) 279 | { 280 | ulHalfLength = ulGroupLength; 281 | ulGroupLength *= 2; 282 | for(unsigned long j = 0; j < ulN; j += ulGroupLength) 283 | { 284 | for(unsigned long k = 0; k < ulHalfLength; k++) 285 | { 286 | cw = vecW[k * ulN / ulGroupLength] * vecList[j + k + ulHalfLength]; 287 | c1 = vecList[j + k] + cw; 288 | c2 = vecList[j + k] - cw; 289 | vecList[j + k] = c1; 290 | vecList[j + k + ulHalfLength] = c2; 291 | } 292 | } 293 | } 294 | } 295 | 296 | void WaveFunction::Filt(float *spdata, float *FiltCoe1, float *FiltCoe2, int *Num, float *En, vector > &vecList){ 297 | float temp=0; 298 | int id, id1, id2; 299 | 300 | for(id = 0 ; id <= FiltNum ; id++) 301 | { 302 | En[id]=0.0F; 303 | } 304 | for(id = 0 ; id < FFTLen/2 ; id++) 305 | { 306 | temp = vecList[id].real()*vecList[id].real()+vecList[id].imag()*vecList[id].imag(); 307 | id1 = Num[id]; 308 | id2 = id1+1; 309 | En[id1] = En[id1] + FiltCoe1[id] * temp; 310 | En[id2] = En[id2] + FiltCoe2[id] * temp; 311 | } 312 | for(id = 1 ; id <= FiltNum ; id++) 313 | { 314 | if(En[id]!=0) 315 | En[id]=(float)log(En[id]); 316 | } 317 | } 318 | 319 | void WaveFunction::MFCC(float *En) 320 | { 321 | int idcep, iden; 322 | float Cep[MFCC_P]; 323 | 324 | for(idcep = 0 ; idcep < MFCC_P ; idcep++) 325 | { 326 | Cep[idcep] = 0.0; 327 | 328 | for(iden = 1 ; iden <= FiltNum ; iden++) 329 | { 330 | Cep[idcep] = Cep[idcep] + En[iden] * (float)cos((idcep+1) * (iden-0.5F) * PI/(FiltNum)); 331 | } 332 | Cep[idcep] = Cep[idcep] / 10.0F; 333 | xishu.push_back(Cep[idcep]); 334 | } 335 | } 336 | 337 | 338 | float WaveFunction::ComputeDTW(float *cep1, float *cep2, int num1, int num2){ 339 | struct record 340 | { int x; 341 | int y; 342 | }; 343 | struct point 344 | { int x,y; 345 | float minvalue; 346 | int stepnum; 347 | bool recheck; //记录该点是否被记录过 348 | }; 349 | record * re; 350 | record * newre; 351 | 352 | newre=new record[num1*num2]; //记录下一层的所有点 353 | re=new record[num1*num2]; //记录当层的所有点 354 | int renum; 355 | int newrenum=0; 356 | int i,j; 357 | point * poi; 358 | poi=new point[num1*num2]; 359 | 360 | for(i=0;i=num1||newy>=num2) 390 | continue; 391 | if(fabs(newx-newy)<=fabs(num1-num2)+3) 392 | { 393 | if(poi[(newy-1)*num1+newx-1].recheck==0) 394 | { 395 | newre[newrenum].x=newx; 396 | newre[newrenum].y=newy; 397 | newrenum++; 398 | } 399 | float tmpdis; 400 | int addstepnum; 401 | if(j==0){ tmpdis=Distance(cep1,cep2,newx-1,newy-1)*2+Distance(cep1,cep2,newx,newy); addstepnum=2;} 402 | if(j==1){ tmpdis=Distance(cep1,cep2,newx,newy)*2; addstepnum=1;} 403 | if(j==2){ tmpdis=Distance(cep1,cep2,newx-1,newy-1)*2+Distance(cep1,cep2,newx,newy); addstepnum=2;} 404 | if(poi[(newy-1)*num1+newx-1].minvalue>(poi[(re[i].y-1)*num1+re[i].x-1].minvalue+tmpdis)) 405 | { 406 | poi[(newy-1)*num1+newx-1].minvalue=(poi[(re[i].y-1)*num1+re[i].x-1].minvalue+tmpdis); 407 | poi[(newy-1)*num1+newx-1].stepnum=poi[(re[i].y-1)*num1+re[i].x-1].stepnum+addstepnum; 408 | } 409 | if(poi[(newy-1)*num1+newx-1].recheck==0) 410 | poi[(newy-1)*num1+newx-1].recheck=1; 411 | } 412 | } 413 | if(newrenum!=0 && i>=(renum-1)) 414 | { 415 | renum=newrenum; 416 | newrenum=0; 417 | struct record * tt; 418 | tt=re; 419 | re=newre; 420 | newre=tt; 421 | i=-1; 422 | } 423 | } 424 | float min=INF; 425 | for(j=0;j >& vecList); 20 | 21 | //傅里叶变换 22 | void FFT(const unsigned long & ulN, vector >& vecList); 23 | 24 | /* 25 | 根据滤波器参数计算频带能量 26 | 输入参数:*spdata ---预处理之后的一帧语音信号 27 | *FiltCoe1---三角形滤波器左边的系数 28 | *FiltCoe2---三角形滤波器右边的系数 29 | *Num ---决定每个点属于哪一个滤波器 30 | 31 | 输出参数:*En ---输出对数频带能量 32 | */ 33 | void Filt(float *spdata, float *FiltCoe1, float *FiltCoe2, int *Num, float *En,vector >& vecList); 34 | 35 | /* 36 | 计算MFCC系数 37 | 输入参数:*En ---对数频带能量 38 | */ 39 | void MFCC(float *En); 40 | 41 | 42 | float ComputeDTW(float *cep1, float *cep2, int num1, int num2); 43 | float Distance(float * ps1,float * ps2,int k1,int k2); 44 | 45 | void AdjustSize(); 46 | 47 | private: 48 | vector xishu; 49 | double *Hamming; 50 | vector > SourceMFCCs; 51 | int MFCC_P; 52 | int MFCC_Pf; 53 | int FrmLen; 54 | int FFTLen; 55 | 56 | public: 57 | vector > getMFCCs(string filename); 58 | vector > addFirstOrderDifference(vector > mfccs); 59 | vector > addOrderDifference(vector > mfccs); 60 | 61 | //输入为两个mfcc参数,cep1,cep2,返回最短距离 62 | float ComputeDTW(vector > cep1,vector > cep2); 63 | WaveFunction(int frm_len, int mfcc_num); 64 | ~WaveFunction(); 65 | }; 66 | 67 | #endif // WAVEFUNCTION_H 68 | -------------------------------------------------------------------------------- /WaveStructure.h: -------------------------------------------------------------------------------- 1 | #ifndef WAVESTRUCTURE_H 2 | #define WAVESTRUCTURE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | using namespace std; 13 | #define INF 1000000000000 14 | const float PI=3.1415926536; 15 | const int FS=8; 16 | const int FiltNum=25; 17 | 18 | struct RIFF_HEADER{ 19 | char szRiffID[4]; // 'R','I','F','F' 20 | unsigned long dwRiffSize; 21 | char szRiffFormat[4]; // 'W','A','V','E' 22 | }; 23 | 24 | struct FMT_HEADER{ 25 | char szFmtID[4]; // 'f','m','t',' ' 26 | unsigned long dwFmtSize; // 18 OR 16 27 | }; 28 | 29 | struct DATA_BLOCK{ 30 | char szDataID[4]; // 'd','a','t','a' 31 | unsigned long dwDataSize; 32 | }; 33 | 34 | struct WAVE_FORMAT{ 35 | unsigned short wFormatTag; 36 | unsigned short wChannels; 37 | unsigned long dwSamplesPerSec; 38 | unsigned long dwAvgBytesPerSec; 39 | unsigned short wBlockAlign; 40 | unsigned short wBitsPerSample; 41 | unsigned short wAppendData; 42 | }; 43 | 44 | struct mTWavHeader{ 45 | int rId; //标志符(RIFF) 46 | int rLen; //数据大小,包括数据头的大小和音频文件的大小 47 | int wId; //格式类型("WAVE") 48 | int fId; //"fmt" 49 | int fLen; //Sizeof(WAVEFORMATEX) 50 | short wFormatTag; //编码格式,包括WAVE_FORMAT_PCM,WAVEFORMAT_ADPCM等 51 | short nChannels; //声道数,单声道为1,双声道为2 52 | int nSamplesPerSec; //采样频率 53 | int nAvgBytesPerSec; //每秒的数据量 54 | short nBlockAlign; //块对齐 55 | short wBitsPerSample; //WAVE文件的采样大小 56 | int dId; //"data" 57 | int wSampleLength; //音频数据的大小 58 | }; 59 | 60 | #endif // WAVESTRUCTURE_H 61 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include"WaveFunction.h" 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | WaveFunction* a=new WaveFunction(128,13);//每帧多少个采样点,MFCC参数的维数 6 | 7 | vector > mfccs1 = a->getMFCCs("D:\\1.wav");//提取mfcc参数 8 | vector > mfccs2 = a->getMFCCs("D:\\2.wav"); 9 | 10 | //mfccs1=a->addFirstOrderDifference(mfccs1);//增加一阶差分系数,此时mfcc参数变为13+13维 11 | 12 | mfccs1=a->addOrderDifference(mfccs1);//增加一阶差分和二阶差分系数,此时mfcc参数变为13*2+13维 13 | mfccs2=a->addOrderDifference(mfccs2); 14 | 15 | 16 | cout<ComputeDTW(mfccs1,mfccs2);//利用动态时间规整算法,计算两个语音的相似度,越小相似度越大 17 | 18 | return 0; 19 | } 20 | --------------------------------------------------------------------------------