2017-09-08 20:59:01 lzc504603913 阅读数 1791
  • 数据结构与算法C/C++实现)视频教程

    C/C++实现数据结构与算法视频培训课程全面介绍计算机行业,基本的数据结构与算法,既有理论的深度也有实战的技法。课程全程讲师手敲代码,一步步代你走进数据结构与算法。 本课程涉及的数据结构与算法有,栈,队列,单向链表,双向循环链表,树,二叉树,搜索二叉树,平衡搜索二叉树,冒泡,选择,直插,希尔,,归并等,课程还涉及深度优先算法与广度优先算法等等。

    3981 人正在学习 去看看 王桂林


MFCC-DTW

Simple MFCC extractor and an speech recognition algorithm (Dynamic Time Warping)

一个MFCC参数提取模板,和语音识别算法(DTW)


main.cpp里有详细的用法,提取原理请参考其他博客。识别算法介绍请参考其他博客。抽象成一个类后,我们不必关心内部的实现方法,直接调用函数提取mfcc参数即可。


Github地址:https://github.com/Linzecong/MFCC-DTW



WaveStructure.h

#ifndef WAVESTRUCTURE_H
#define WAVESTRUCTURE_H

#include<vector>
#include<complex>
#include<cmath>
#include<string>
#include<iostream>
#include<stdlib.h>
#include<malloc.h>
#include<bitset>
using   namespace   std;
#define INF 1000000000000
const float PI=3.1415926536;
const int FS=8;
const int FiltNum=25;

struct RIFF_HEADER{
   char szRiffID[4];  // 'R','I','F','F'
   unsigned long dwRiffSize;
   char szRiffFormat[4]; // 'W','A','V','E'
};

struct FMT_HEADER{
   char  szFmtID[4]; // 'f','m','t',' '
   unsigned long  dwFmtSize; // 18 OR 16
};

struct DATA_BLOCK{
    char szDataID[4]; // 'd','a','t','a'
    unsigned long dwDataSize;
};

struct WAVE_FORMAT{
   unsigned short  wFormatTag;
   unsigned short  wChannels;
   unsigned long dwSamplesPerSec;
   unsigned long dwAvgBytesPerSec;
   unsigned short  wBlockAlign;
   unsigned short  wBitsPerSample;
   unsigned short  wAppendData;
};

struct mTWavHeader{
        int rId;    //标志符(RIFF)
        int rLen;   //数据大小,包括数据头的大小和音频文件的大小
        int wId;    //格式类型("WAVE")
        int fId;    //"fmt"
        int fLen;   //Sizeof(WAVEFORMATEX)
        short wFormatTag;       //编码格式,包括WAVE_FORMAT_PCM,WAVEFORMAT_ADPCM等
        short nChannels;        //声道数,单声道为1,双声道为2
        int nSamplesPerSec;   //采样频率
        int nAvgBytesPerSec;  //每秒的数据量
        short nBlockAlign;      //块对齐
        short wBitsPerSample;   //WAVE文件的采样大小
        int dId;              //"data"
        int wSampleLength;    //音频数据的大小
};

#endif // WAVESTRUCTURE_H


WaveFunction.h


#ifndef WAVEFUNCTION_H
#define WAVEFUNCTION_H
#include"WaveStructure.h"

class WaveFunction{
private:
    int CharCmp(const char *first,const char *second,unsigned short len);
    void InitHamming();

    //设置滤波器参数
    //输入参数:无
    //输出参数:FiltCoe1,三角形滤波器左边的系数,FiltCoe2三角形滤波器右边的系数,Num决定每个点属于哪一个滤波器
    void InitFilt(float *FiltCoe1, float *FiltCoe2, int *Num);

    //加窗,输入为buf,输出为data
    void HammingWindow(short* buf,float* data);

    //计算傅里叶参数
    void ComputeFFT(float *buffer,vector<complex<float> >& vecList);

    //傅里叶变换
    void FFT(const unsigned long & ulN, vector<complex<float> >& vecList);

    /*
    根据滤波器参数计算频带能量
    输入参数:*spdata  ---预处理之后的一帧语音信号
              *FiltCoe1---三角形滤波器左边的系数
              *FiltCoe2---三角形滤波器右边的系数
              *Num     ---决定每个点属于哪一个滤波器
    输出参数:*En      ---输出对数频带能量
    */
    void Filt(float *spdata, float *FiltCoe1, float *FiltCoe2, int *Num, float *En,vector<complex<float> >& vecList);

    /*
    计算MFCC系数
    输入参数:*En ---对数频带能量
    */
    void MFCC(float *En);


    float ComputeDTW(float *cep1, float *cep2, int num1, int num2);
    float Distance(float * ps1,float * ps2,int k1,int k2);

    void AdjustSize();

private:
    vector<float> xishu;
    double *Hamming;
    vector<vector<float> > SourceMFCCs;
    int MFCC_P;
    int MFCC_Pf;
    int FrmLen;
    int FFTLen;

public:
    vector<vector<float> > getMFCCs(string filename);
    vector<vector<float> > addFirstOrderDifference(vector<vector<float> > mfccs);
    vector<vector<float> > addOrderDifference(vector<vector<float> > mfccs);

    //输入为两个mfcc参数,cep1,cep2,返回最短距离
    float ComputeDTW(vector<vector<float> > cep1,vector<vector<float> > cep2);
    WaveFunction(int frm_len, int mfcc_num);
    ~WaveFunction();
};

#endif // WAVEFUNCTION_H


WaveFunction.cpp

#include "WaveFunction.h"

WaveFunction::WaveFunction(int frm_len, int mfcc_num){
    MFCC_P=mfcc_num;
    MFCC_Pf=float(mfcc_num);
    FrmLen=frm_len;
    FFTLen=frm_len;
    Hamming=new double[FrmLen];
}

WaveFunction::~WaveFunction(){
    delete []Hamming;
}

vector<vector<float> > WaveFunction::getMFCCs(string filename){
    xishu.clear();
    SourceMFCCs.clear();
    //mfcc分析
    mTWavHeader waveheader;
    FILE *sourcefile;
    short buffer[FrmLen];
    float data[FrmLen];
    float FiltCoe1[FFTLen/2+1];  //左系数
    float FiltCoe2[FFTLen/2+1];  //右系数
    int Num[FFTLen/2+1];     //决定每个点属于哪一个滤波器
    float En[FiltNum+1];         //频带能量
    vector<complex<float> > vecList;

    sourcefile=fopen(filename.c_str(),"rb");
    fread(&waveheader,sizeof(mTWavHeader),1,sourcefile);
    InitHamming();//初始化汉明窗
    InitFilt(FiltCoe1,FiltCoe2,Num); //初始化MEL滤波系数

    while(fread(buffer,sizeof(short),FrmLen,sourcefile)==FrmLen){
        HammingWindow(buffer,data);
        ComputeFFT(data,vecList);
        Filt(data, FiltCoe1, FiltCoe2, Num, En,vecList);
        MFCC(En);
        vecList.clear();
        fseek(sourcefile, -FrmLen/2, SEEK_CUR);//考虑到帧移,每次移动半帧
    }

    int stdlength=xishu.size();

    for(int i=0;i<stdlength/MFCC_P;i++){
        vector<float> temp;
        for(int j=0;j<MFCC_P;j++)
            temp.push_back(xishu[i*MFCC_P+j]);
        SourceMFCCs.push_back(temp);
    }
    fclose(sourcefile);
    return SourceMFCCs;
}

vector<vector<float> > addFirstOrderDifference(vector<vector<float> > mfccs){
    vector<vector<float> > temp;
    for(int i=0;i<mfccs.size();i++){
        vector<float> line=mfccs[i];
        int size=line.size();
        for(int t=0;t<size;t++){
            if(t<2)
                line.push_back(line[t+1]-line[t]);
            else{
                if(t>size-2||t==size-2)
                    line.push_back(line[t]-line[t-1]);
                else{
                    float fenzi=line[t+1]-line[t-1]+2*(line[t+2]-line[t-2]);
                    float fenmu=sqrtf(10);
                    line.push_back(fenzi/fenmu);

                }
            }
        }
        temp.push_back(line);
    }
    return temp;
}

vector<vector<float> > addOrderDifference(vector<vector<float> > mfccs){
    vector<vector<float> > temp;
    for(int i=0;i<mfccs.size();i++){
        vector<float> line=mfccs[i];
        int size=line.size();
        //一阶差分
        for(int t=0;t<size;t++){
            if(t<2)
                line.push_back(line[t+1]-line[t]);
            else{
                if(t>size-2||t==size-2)
                    line.push_back(line[t]-line[t-1]);
                else{
                    float fenzi=line[t+1]-line[t-1]+2*(line[t+2]-line[t-2]);
                    float fenmu=sqrtf(10);
                    line.push_back(fenzi/fenmu);

                }
            }
        }
        //二阶差分
        for(int t=size;t<size*2;t++){
            if(t<2)
                line.push_back(line[t+1]-line[t]);
            else{
                if(t>size-2||t==size-2)
                    line.push_back(line[t]-line[t-1]);
                else{
                    float fenzi=line[t+1]-line[t-1]+2*(line[t+2]-line[t-2]);
                    float fenmu=sqrtf(10);
                    line.push_back(fenzi/fenmu);

                }
            }
        }
        temp.push_back(line);
    }
    return temp;
}

float WaveFunction::ComputeDTW(vector<vector<float> > cep1, vector<vector<float> > cep2)
{
    vector<float> temp;
    for(int i=0;i<cep1.size();i++)
        for(int j=0;j<cep1[i].size();j++)
            temp.push_back(cep1[i][j]);
    int stdlength=temp.size();
    float * stdmfcc = new float[stdlength];
    std::copy(temp.begin(),temp.end(),stdmfcc);

    vector<float> temp1;
    for(int i=0;i<cep2.size();i++)
        for(int j=0;j<cep2[i].size();j++)
            temp1.push_back(cep2[i][j]);
    int testlen=temp1.size();
    float * testmfcc = new float[testlen];
    std::copy(temp1.begin(),temp1.end(),testmfcc);
    return ComputeDTW(stdmfcc,testmfcc,stdlength/MFCC_P,testlen/MFCC_P);
}

int WaveFunction::CharCmp(const char *first,const char *second,unsigned short len)
{
    int i=0;
    while((first[i]==second[i])&&(i++<len));
    if(i>=len)
        return 0;
    else if(first[i-1]>second[i-1])
        return 1;
    else
        return -1;
}

void WaveFunction::InitHamming(){

    float twopi;
    int i;
    twopi=8.0F*atan(1.0F);
    for(i=0;i<FrmLen;i++)
        Hamming[i]=(float)(0.54-0.46*cos((float)i*twopi/(float)(FrmLen-1)));
}

void WaveFunction::InitFilt(float *FiltCoe1, float *FiltCoe2, int *Num){
    int i,j;
    float Freq;
    int FiltFreq[FiltNum+1] = {0,100,200,300,400,500,600,700,800,900,1000,
                               1149,1320,1516,1741,2000,2297,2639,3031,3482,4000,
                               4595,5278,6063,6964,8001};//滤波器的中心频率
    int BW[FiltNum+1]={100,100,100,100,100,100,100,100,100,100,124,
                       160,184,211,242,278,320,367,422,484,556,
                       639,734,843,969,1112};//滤波器的带宽
    for(i = 0 ; i<= FFTLen/2 ; i++ )
    {
        Num[i]=0;
    }

    for(i = 0 ; i <= FFTLen/2 ; i++)
    {
        Freq = FS * 1000.0F * (float)(i) / (float)(FFTLen);
        for(j = 0 ; j <FiltNum ; j++)
        {
            if(Freq >= (float)FiltFreq[j] && Freq <= (float)FiltFreq[j+1])
            {
                Num[i] = j;
                if(j == 0)
                {
                    FiltCoe1[i] = 0.0F;
                }
                else
                {
                    FiltCoe1[i] = ((float)(FiltFreq[j]+BW[j])-Freq) / (float)(BW[j]);
                }
                FiltCoe2[i] = (Freq-(float)(FiltFreq[j+1]-BW[j+1])) / (float)(BW[j+1]);
                FiltCoe1[i] = FiltCoe1[i] * FiltCoe1[i];
                FiltCoe2[i] = FiltCoe2[i] * FiltCoe2[i];
                break;
            }
        }
    }

}

void WaveFunction::HammingWindow(short *buf, float *data){
    int i;
    for(i=0;i<FrmLen;i++)
        data[i]=buf[i]*Hamming[i];
}

void WaveFunction::ComputeFFT(float *data, vector<complex<float> > &vecList){
    for(int i=0;i<FFTLen;++i)
    {
        if(i<FrmLen)
        {
            complex<float> temp(data[i]);
            vecList.push_back(temp);
        }
        else
        {
            complex<float> temp(0);
            vecList.push_back(temp);
        }
    }
    FFT(FFTLen,vecList);
}

void WaveFunction::FFT(const unsigned long &ulN, vector<complex<float> > &vecList){
    //得到幂数

    unsigned long ulPower = 0; //幂数
    unsigned long ulN1 = ulN - 1;
    while(ulN1 > 0)
    {
        ulPower++;
        ulN1 /= 2;
    }
    //反序

    bitset<sizeof(unsigned long) * 8> bsIndex; //二进制容器
    unsigned long ulIndex; //反转后的序号
    unsigned long ulK;
    for(unsigned long p = 0; p < ulN; p++)
    {
        ulIndex = 0;
        ulK = 1;
        bsIndex = bitset<sizeof(unsigned long) * 8>(p);
        for(unsigned long j = 0; j < ulPower; j++)
        {
            ulIndex += bsIndex.test(ulPower - j - 1) ? ulK : 0;
            ulK *= 2;
        }

        if(ulIndex > p)
        {
            complex<float> c = vecList[p];
            vecList[p] = vecList[ulIndex];
            vecList[ulIndex] = c;
        }
    }

    //计算旋转因子

    vector<complex<float> > vecW;
    for(unsigned long i = 0; i < ulN / 2; i++)
    {
        vecW.push_back(complex<float>(cos(2 * i * PI / ulN) , -1 * sin(2 * i * PI / ulN)));
    }

    /*for(unsigned long m = 0; m < ulN / 2; m++)
    {
        cout<< "\nvW[" << m << "]=" << vecW[m];
    } */

    //计算FFT

    unsigned long ulGroupLength = 1; //段的长度
    unsigned long ulHalfLength = 0; //段长度的一半
    unsigned long ulGroupCount = 0; //段的数量
    complex<float> cw; //WH(x)
    complex<float> c1; //G(x) + WH(x)
    complex<float> c2; //G(x) - WH(x)
    for(unsigned long b = 0; b < ulPower; b++)
    {
        ulHalfLength = ulGroupLength;
        ulGroupLength *= 2;
        for(unsigned long j = 0; j < ulN; j += ulGroupLength)
        {
            for(unsigned long k = 0; k < ulHalfLength; k++)
            {
                cw = vecW[k * ulN / ulGroupLength] * vecList[j + k + ulHalfLength];
                c1 = vecList[j + k] + cw;
                c2 = vecList[j + k] - cw;
                vecList[j + k] = c1;
                vecList[j + k + ulHalfLength] = c2;
            }
        }
    }
}

void WaveFunction::Filt(float *spdata, float *FiltCoe1, float *FiltCoe2, int *Num, float *En, vector<complex<float> > &vecList){
    float temp=0;
    int id, id1, id2;

    for(id = 0 ; id <= FiltNum ; id++)
    {
        En[id]=0.0F;
    }
    for(id = 0 ; id < FFTLen/2 ; id++)
    {
        temp = vecList[id].real()*vecList[id].real()+vecList[id].imag()*vecList[id].imag();
        id1 = Num[id];
        id2 = id1+1;
        En[id1] = En[id1] + FiltCoe1[id] * temp;
        En[id2] = En[id2] + FiltCoe2[id] * temp;
    }
    for(id = 1 ; id <= FiltNum ; id++)
    {
        if(En[id]!=0)
            En[id]=(float)log(En[id]);
    }
}

void WaveFunction::MFCC(float *En)
{
    int idcep, iden;
    float Cep[MFCC_P];

    for(idcep = 0 ; idcep < MFCC_P ; idcep++)
    {
        Cep[idcep] = 0.0;

        for(iden = 1 ; iden <= FiltNum ; iden++)
        {
            Cep[idcep] = Cep[idcep] + En[iden] * (float)cos((idcep+1) * (iden-0.5F) * PI/(FiltNum));
        }
        Cep[idcep] = Cep[idcep] / 10.0F;
        xishu.push_back(Cep[idcep]);
    }
}


float WaveFunction::ComputeDTW(float *cep1, float *cep2, int num1, int num2){
    struct record
    {		int x;
                int y;
    };
    struct point
    {		int x,y;
                float minvalue;
                        int stepnum;
                                bool recheck;               //记录该点是否被记录过
    };
    record * re;
    record * newre;

    newre=new record[num1*num2];    //记录下一层的所有点
    re=new record[num1*num2];       //记录当层的所有点
    int renum;
    int newrenum=0;
    int i,j;
    point * poi;
    poi=new point[num1*num2];

    for(i=0;i<num1*num2;i++)
    {
        poi[i].recheck=0;
        poi[i].minvalue=INF;
        poi[i].stepnum=0;
    }								//设置初始值

    for(i=0;i<5;i++)                //起始点
    {
        if(i==0)  {	re[i].x=1; re[i].y=1; }
        if(i==1)  {	re[i].x=1; re[i].y=2; }
        if(i==2)  {	re[i].x=1; re[i].y=3; }
        if(i==3)  {	re[i].x=2; re[i].y=1; }
        if(i==4)  {	re[i].x=3; re[i].y=1; }
        poi[(re[i].y-1)*num1+re[i].x-1].minvalue=Distance(cep1,cep2,re[i].x,re[i].y);
        poi[(re[i].y-1)*num1+re[i].x-1].stepnum=1;
    }
    renum=5;
    int newx,newy;                   //newvalue;
    for(i=0;i<renum;i++)
    {
        for(j=0;j<3;j++)
        {
            if(j==0){ newx=re[i].x+1; newy=re[i].y+2; }
            if(j==1){ newx=re[i].x+1; newy=re[i].y+1; }
            if(j==2){ newx=re[i].x+2; newy=re[i].y+1; }

            /////////////三种可能路径

            if(newx>=num1||newy>=num2)
                continue;
            if(fabs(newx-newy)<=fabs(num1-num2)+3)
            {
                if(poi[(newy-1)*num1+newx-1].recheck==0)
                {
                    newre[newrenum].x=newx;
                    newre[newrenum].y=newy;
                    newrenum++;
                }
                float tmpdis;
                int addstepnum;
                if(j==0){ tmpdis=Distance(cep1,cep2,newx-1,newy-1)*2+Distance(cep1,cep2,newx,newy); addstepnum=2;}
                if(j==1){ tmpdis=Distance(cep1,cep2,newx,newy)*2; addstepnum=1;}
                if(j==2){ tmpdis=Distance(cep1,cep2,newx-1,newy-1)*2+Distance(cep1,cep2,newx,newy); addstepnum=2;}
                if(poi[(newy-1)*num1+newx-1].minvalue>(poi[(re[i].y-1)*num1+re[i].x-1].minvalue+tmpdis))
                {
                    poi[(newy-1)*num1+newx-1].minvalue=(poi[(re[i].y-1)*num1+re[i].x-1].minvalue+tmpdis);
                    poi[(newy-1)*num1+newx-1].stepnum=poi[(re[i].y-1)*num1+re[i].x-1].stepnum+addstepnum;
                }
                if(poi[(newy-1)*num1+newx-1].recheck==0)
                    poi[(newy-1)*num1+newx-1].recheck=1;
            }
        }
        if(newrenum!=0 && i>=(renum-1))
        {
            renum=newrenum;
            newrenum=0;
            struct	record * tt;
            tt=re;
            re=newre;
            newre=tt;
            i=-1;
        }
    }
    float min=INF;
    for(j=0;j<renum;j++)
    {
        if((poi[(re[j].y-1)*num1+re[j].x-1].minvalue)/poi[(re[j].y-1)*num1+re[j].x-1].stepnum<min)
            min=(poi[(re[j].y-1)*num1+re[j].x-1].minvalue)/poi[(re[j].y-1)*num1+re[j].x-1].stepnum;
    }

    //	min;
    delete []poi;
    delete []newre;
    delete []re;
    delete []cep1;
    delete []cep2;
    return min;
}

float WaveFunction::Distance(float *ps1, float *ps2, int k1, int k2){
    int i=0;
    float sum=0;
    for(i=0;i<MFCC_P;i++)
        sum+=(1+MFCC_Pf/2*(float)sin(PI*i/MFCC_Pf))*(ps1[k1+i]-ps2[k2+i])*(ps1[k1+i]-ps2[k2+i]);

    return sum;
}

main.cpp

#include"wavefunction.h"

int main(int argc, char *argv[])
{
    WaveFunction* a=new WaveFunction(128,13);//每帧多少个采样点,MFCC参数的维数
    
    vector<vector<float> > mfccs1 = a->getMFCCs("D:\\1.wav");//提取mfcc参数
    vector<vector<float> > mfccs2 = a->getMFCCs("D:\\2.wav");

    //mfccs1=a->addFirstOrderDifference(mfccs1);//增加一阶差分系数,此时mfcc参数变为13+13维

    mfccs1=a->addOrderDifference(mfccs1);//增加一阶差分和二阶差分系数,此时mfcc参数变为13*2+13维
    mfccs2=a->addOrderDifference(mfccs2);

    
    cout<<a->ComputeDTW(mfccs1,mfccs2);//利用动态时间规整算法,计算两个语音的相似度,越小相似度越大

    return 0;
}



2016-11-28 23:25:39 qq_34369618 阅读数 6247
  • 数据结构与算法C/C++实现)视频教程

    C/C++实现数据结构与算法视频培训课程全面介绍计算机行业,基本的数据结构与算法,既有理论的深度也有实战的技法。课程全程讲师手敲代码,一步步代你走进数据结构与算法。 本课程涉及的数据结构与算法有,栈,队列,单向链表,双向循环链表,树,二叉树,搜索二叉树,平衡搜索二叉树,冒泡,选择,直插,希尔,,归并等,课程还涉及深度优先算法与广度优先算法等等。

    3981 人正在学习 去看看 王桂林

C++语音识别接口快速入门(Microsoft Speech SDK)
尤其注意其中的宽字符串转化

#include <iostream>
#include <sapi.h> //导入语音头文件
#include <string>
#pragma comment(lib,"sapi.lib") //导入语音头文件库

void  MSSSpeak(LPCTSTR speakContent)// speakContent为LPCTSTR型的字符串,调用此函数即可将文字转为语音
{
    ISpVoice *pVoice = NULL;

    //初始化COM接口

    if (FAILED(::CoInitialize(NULL)))
        MessageBox(NULL, (LPCWSTR)L"COM接口初始化失败!", (LPCWSTR)L"提示", MB_ICONWARNING | MB_CANCELTRYCONTINUE | MB_DEFBUTTON2);

    //获取SpVoice接口

    HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void**)&pVoice);


    if (SUCCEEDED(hr))
    {
        pVoice->SetVolume((USHORT)100); //设置音量,范围是 0 -100
        pVoice->SetRate(0); //设置速度,范围是 -10 - 10
        hr = pVoice->Speak(speakContent, 0, NULL);

        pVoice->Release();

        pVoice = NULL;
    }

    //释放com资源
    ::CoUninitialize();
}
int main()
{
    std::wstring a = L"我爱谢贤";
    LPCWSTR str = a.c_str();
    /*不知道为什么Cstr不行*/
    MSSSpeak(str);
    return 0;
}
2019-04-09 20:16:52 u012594175 阅读数 771
  • 数据结构与算法C/C++实现)视频教程

    C/C++实现数据结构与算法视频培训课程全面介绍计算机行业,基本的数据结构与算法,既有理论的深度也有实战的技法。课程全程讲师手敲代码,一步步代你走进数据结构与算法。 本课程涉及的数据结构与算法有,栈,队列,单向链表,双向循环链表,树,二叉树,搜索二叉树,平衡搜索二叉树,冒泡,选择,直插,希尔,,归并等,课程还涉及深度优先算法与广度优先算法等等。

    3981 人正在学习 去看看 王桂林

根据语音片段进行说话人识别的SDK,c/c++实现。

利用ivector算法,进行说话人识别,包含了一个基础的ivector模型(6M),效果一般优于gmm-ubm算法。

wav文件不要超过1M大小。

1.Windows 

Win10 and VS2013

环境配置参考《说话人识别和说话人性别识别SDK-通过语音判断说话人,及说话人的性别》。因为ivector比gmm-ubm计算量更大,debug模式可能会比较慢。

// main.cpp

#include <iostream>
#include "speaker_recognizer.h"
int main()
{
	SpeakerRecognizer spk;
	bool ret = spk.Init("iv.model");
	if (!ret)
		return -1;

	ret = spk.RegisterSpeaker("BAC009S0002W0123.wav", "s2");
	
	ret = spk.RegisterSpeaker("BAC009S0003W0121.wav", "s3");

	std::string rec = spk.RecognizeSpeaker("BAC009S0003W0122.wav"); // s3
	std::cout << rec << std::endl;

	return 0;
}

2.Android

待更新...

 

https://github.com/NonDay/FreeSR

完整代码下载地址:https://download.csdn.net/download/u012594175/11100773

声纹识别交流QQ群:875705987

2018-06-23 20:38:00 bvngh3247 阅读数 1423
  • 数据结构与算法C/C++实现)视频教程

    C/C++实现数据结构与算法视频培训课程全面介绍计算机行业,基本的数据结构与算法,既有理论的深度也有实战的技法。课程全程讲师手敲代码,一步步代你走进数据结构与算法。 本课程涉及的数据结构与算法有,栈,队列,单向链表,双向循环链表,树,二叉树,搜索二叉树,平衡搜索二叉树,冒泡,选择,直插,希尔,,归并等,课程还涉及深度优先算法与广度优先算法等等。

    3981 人正在学习 去看看 王桂林

如何使用kaldi系统

Kaldi运行流程

前提: 你已经有了一定数量的包含不同说话人的的数字音频数据, 每一个音频文件是一个完整的句子。我们以清华30小时为例。
目的: 你想把你的音频数据分成训练部分和测试部分,搭建一个ASR系统并且对它进行训练和测试,得到一些解码结果。
首要任务:首先在 kaldi/egs/目录下创建一个名为 thchs30/s5 的文件夹,这是你存放有关你工程的所有文件的地方。
接下来,讲解一下运行流程:

1数据准备

1)首先我们要把录制的音频文件我们所说的语料.wav文件放入/kaldi//thchs30-openslr/data_thchs30/,我们要把数据分成train和test两个文件夹,用于训练和测试用。
2)然后需要准备声学数据
数据一般放在egs/thchs30/s5/data文件夹,里面如下所示:
dev dict dict_phone fbank graph graph_phone lang lang_phone local mfcc test test_phone train
数据分成test和train两个文件夹,两个文件夹里面需要准备一样的东西,如下所示:
phone.txt spk2utt text utt2spk wav.scp word.txt
(1) train文件夹里面的需要手动准备的4个文件
a.)wav.scp这个文件连接每一句话,就是语音号后面接着对应的wav文件的绝对路径,需注意如果“segments”文件不存在,“wav.scp”每一行的第一项就是发音编号。
PATTERN:
A11_000 /home/hujianhua/kaldi//thchs30-openslr/data_thchs30/train/A11_0.wav
A11_001 /home/hujianhua/kaldi//thchs30-openslr/data_thchs30/train/A11_1.wav
A11_010 /home/hujianhua/kaldi//thchs30-openslr/data_thchs30/train/A11_10.wav
A11_100 /home/hujianhua/kaldi//thchs30-openslr/data_thchs30/train/A11_100.wav
A11_102 /home/hujianhua/kaldi//thchs30-openslr/data_thchs30/train/A11_102.wav
b.)text文件包含每段发音的标注。
每行的第一项是发音编号(utterance-id),可以是任意的文本字符串,但是如果在你的设置中还包含说话人信息,你应该把说话人编号(speaker-id)作为发音编号的前缀。
发音编号后面跟着的是每段发音的标注,你不用保证这里出现的每一个字都出现在你的词汇表中。词汇表之外的词会被映射到data/lang/oov.txt中。
PATTERN:
这里写图片描述
c.) utt2spk 这个文件夹告诉 ASR 系统哪一个句子属于哪个特定的说话人
PATTERN:
A11_000 A11
A11_001 A11
A11_010 A11
A11_100 A11
A11_102 A11
d.) spk2utt这个文件夹告诉 ASR 系统哪个特定的说话人说了哪些句子,这个可以通过脚本文件生成。
PATTERN:
A11 A11_000 A11_001 A11_010 A11_100 A11_102
(2)train里面另外其他2个文件,可以通过脚本生成。
a.)phone.txt文件包含着每个文件里面的文字对应的音素标注
这里写图片描述
b.)word.txt文件包含每个文件的文字表示的意思,其实跟text文件里面内容一样。
这里写图片描述
3)准备语言数据
任务:在egs/thchs30/s5/data/目录下,创建一个新的文件夹’dict’。
在 egs/thchs30/s5/data /dict 在创建如下文件:
extra_questions.txt lexiconp.txt lexicon.txt nonsilence_phones.txt optional_silence.txt silence_phones.txt
a.) lexicon.txt 这个文件包含你的字典里的每一个单词的音素
PATTERN: …

这里写图片描述
b.) nonsilence_phones.txt 这个文件列出了你工程中的所有的非静音音素
PATTERN:
—– exemplary nonsilence_phones.txt starts —–
a1
a2
a3
a4
a5
aa
ai1
ai2
ai3
ai4

—- exemplary nonsilence_phones.txt ends —–
c.) silence_phones.txt 这个文件列出了静音音素
PATTERN:
—– exemplary silence_phones.txt starts —–
sil
—– exemplary silence_phones.txt ends —–
d.) optional_silence.txt 这个文件列出了可选择的静音音素
PATTERN:
—– exemplary optional_silence.txt starts —–
sil
—– exemplary optional_silence.txt ends —–
还有两个文件extra_questions.txt lexiconp.txt
1) 文件 extra_questions.txt 包含那些自动产生的问题集之外的一些问题,
$ head /egs/thchs30/s5/data/dict/extra_questions.txt
sil
a1 ai1 an1 ang1 ao1 e1 ei1 en1 eng1 i1 ia1 ian1 iang1 iao1 ie1 in1 ing1 iong1 iu1 ix1 iy1 o1 ong1 ou1 u1 ua1 uai1 uan1 uang1 ueng1 ui1 un1 uo1 v1 van1 ve1 vn1
a2 ai2 an2 ang2 ao2 e2 ei2 en2 eng2 er2 i2 ia2 ian2 iang2 iao2 ie2 in2 ing2 iong2 iu2 ix2 iy2 o2 ong2 ou2 u2 ua2 uai2 uan2 uang2 ui2 un2 uo2 v2 van2 ve2 vn2
a3 ai3 an3 ang3 ao3 e3 ei3 en3 eng3 er3 i3 ia3 ian3 iang3 iao3 ie3 in3 ing3 iong3 iu3 ix3 iy3 o3 ong3 ou3 u3 ua3 uai3 uan3 uang3 ueng3 ui3 un3 uo3 v3 van3 ve3 vn3
a4 ai4 an4 ang4 ao4 e4 ei4 en4 eng4 er4 i4 ia4 ian4 iang4 iao4 ie4 in4 ing4 iong4 iu4 ix4 iy4 iz4 o4 ong4 ou4 u4 ua4 uai4 uan4 uang4 ueng4 ui4 un4 uo4 v4 van4 ve4 vn4
a5 ai5 an5 ang5 ao5 e5 ei5 en5 eng5 er5 i5 ia5 ian5 iang5 iao5 ie5 in5 ing5 iong5 iu5 ix5 iy5 iz5 o5 ong5 ou5 u5 ua5 uai5 uan5 uang5 ueng5 ui5 un5 uo5 v5 van5 ve5 vn5
aa b c ch d ee f g h ii j k l m n oo p q r s sh t uu vv x z zh
2) 注意:lexicon.txt 中,如果一个词有不同发音,则会在不同行中出现多次。如果你想使用发音概率,你需要建立 exiconp.txt 而不是 lexicon.txt。 lexiconp.txt 中第二区域就是概率值。注意,一个通常的做法是,对发音概率进行归一化,使最大的那个概率值为1,而不是使同一个词的所有发音概率加起来等于 1。这样可能会得到更好的结果。

2019-02-19 23:30:48 valada 阅读数 3299
  • 数据结构与算法C/C++实现)视频教程

    C/C++实现数据结构与算法视频培训课程全面介绍计算机行业,基本的数据结构与算法,既有理论的深度也有实战的技法。课程全程讲师手敲代码,一步步代你走进数据结构与算法。 本课程涉及的数据结构与算法有,栈,队列,单向链表,双向循环链表,树,二叉树,搜索二叉树,平衡搜索二叉树,冒泡,选择,直插,希尔,,归并等,课程还涉及深度优先算法与广度优先算法等等。

    3981 人正在学习 去看看 王桂林

Kaldi 是一款语音识别工具,可以快速训练语音识别模型。Kaldi 主要是用 C++ 编写,是用 Shell、Python 和 Perl 来作为胶水进行模型训练,并且 Kaldi 是完全免费开源的。

Kaldi 语音识别模型的快速构建,具有大量语音相关算法以及优质的论坛受到国内外企业和开发者的追捧。

本场 Chat 将以以下几个模块进行延展:

  1. Kaldi 介绍;
  2. Kaldi 环境构建;
  3. Kaldi 目录介绍;
  4. Kaldi 相关 Shell 知识介绍;
  5. Kaldi Egs 介绍;
  6. Kaldi Egs 中 AIShell 例子讲解;
  7. 如何使用独立语音进行语音模型构建。

阅读全文: http://gitbook.cn/gitchat/activity/5c6a4fbdb4e4ea7959820d68

您还可以下载 CSDN 旗下精品原创内容社区 GitChat App ,阅读更多 GitChat 专享技术内容哦。

FtooAtPSkEJwnW-9xkCLqSTRpBKX

Kaldi学习(一)

阅读数 17464

语音识别-DTW算法

阅读数 1661

没有更多推荐了,返回首页