2017-04-05 15:11:39 qq_38125123 阅读数 18215
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5915 人正在学习 去看看 杨波

1、概述

   通过微信小程序wx.startRecord()和wx.stopRecord()接口录音并上传silk录音文件至服务器,通过ffmpeg将silk录音文件转成wav录音文件,再通过百度语音识别 REST API 获取语音识别后的结果。

2、代码实现

   录音和语音文件上传


   node.js服务端接收语音文件代码

   silk文件转wav文件

   我使用的是silk-v3-decoder将silk文件转wav文件


silk-v3-decoder 使用方法


   百度语音识别 REST API识别wav文件

1、通过API Key和Secret Key获取的access_token


通过API Key和Secret Key获取的access_token文档


2、通过token 调用百度语音识别 REST API识别接口


3、语音识别优化

   通过上述操作后,发现识别的内容和实际内容差别很大


百度语音识别 REST API文档

   查看文档可知:采样率:8000/16000 仅支持单通道

   在ffmpeg里对应的设置方式分别是:

   -ar rate 设置采样率

   -ac channels 设置声道数

   修改converter.sh文件,修改为下图所示


修改后的converter.sh文件

2017-08-18 16:55:17 qq_22642239 阅读数 1130
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5915 人正在学习 去看看 杨波

目录

一、安装SDK

安装MicrosoftSpeechPlatformSDK.msi,默认路径安装即可。 
下载路径: 
http://download.csdn.net/detail/michaelliang12/9510691

二、新建工程,配置环境

设置: 
1,属性–配置属性–C/C++–常规–附加包含目录:C:\Program Files\Microsoft SDKs\Speech\v11.0\Include(具体路径与安装路径有关) 
2,属性–配置属性–链接器–输入–附加依赖项:sapi.lib;

三、语音识别代码

语音识别接口可分为文字转语音和语音转文字

1、文字转语音

需要添加的头文件:

#include <sapi.h> //导入语音头文件
#pragma comment(lib,"sapi.lib") //导入语音头文件库
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

函数:

void  CBodyBasics::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(2); //设置速度,范围是 -10 - 10
        hr = pVoice->Speak(speakContent, 0, NULL);   //Speak函数的第二个参数如果设置成SPF_IS_FILENAME  则第一个参数指向一个文本(txt)文件

        pVoice->Release();

        pVoice = NULL;
    }

    //释放com资源
    ::CoUninitialize();
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

2、语音转文字

这个稍微麻烦一点,因为需要实时监控麦克风,涉及到windows的消息机制。 
(1)首先设置工程属性: 
属性–配置属性–C/C++–预处理器–预处理器定义:_WIN32_DCOM;

(2)需要添加的头文件:

#include <sapi.h> //导入语音头文件
#pragma comment(lib,"sapi.lib") //导入语音头文件库
#include <sphelper.h>//语音识别头文件
#include <atlstr.h>//要用到CString

#pragma once
const int WM_RECORD = WM_USER + 100;//定义消息
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

(3)在程序的.h头文件中定义变量

//定义变量
CComPtr<ISpRecognizer>m_cpRecoEngine;// 语音识别引擎(recognition)的接口。
CComPtr<ISpRecoContext>m_cpRecoCtxt;// 识别引擎上下文(context)的接口。
CComPtr<ISpRecoGrammar>m_cpCmdGrammar;// 识别文法(grammar)的接口。
CComPtr<ISpStream>m_cpInputStream;// 流()的接口。
CComPtr<ISpObjectToken>m_cpToken;// 语音特征的(token)接口。
CComPtr<ISpAudio>m_cpAudio;// 音频(Audio)的接口。(用来保存原来默认的输入流)
ULONGLONG  ullGrammerID;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

(4)创建语音识别初始化函数(程序刚开始执行的时候调用,例如文末示例代码中,将此初始化函数放在对话框初始化消息WM_INITDIALOG的响应代码里)

//语音识别初始化函数
void  CBodyBasics::MSSListen()
{

    //初始化COM接口

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


    HRESULT hr = m_cpRecoEngine.CoCreateInstance(CLSID_SpSharedRecognizer);//创建Share型识别引擎
    if (SUCCEEDED(hr))
    {


        hr = m_cpRecoEngine->CreateRecoContext(&m_cpRecoCtxt);//创建识别上下文接口

        hr = m_cpRecoCtxt->SetNotifyWindowMessage(m_hWnd, WM_RECORD, 0, 0);//设置识别消息

        const ULONGLONG ullInterest = SPFEI(SPEI_SOUND_START) | SPFEI(SPEI_SOUND_END) | SPFEI(SPEI_RECOGNITION);//设置我们感兴趣的事件
        hr = m_cpRecoCtxt->SetInterest(ullInterest, ullInterest);

        hr = SpCreateDefaultObjectFromCategoryId(SPCAT_AUDIOIN, &m_cpAudio);
        m_cpRecoEngine->SetInput(m_cpAudio, true);



        //创建语法规则
        //dictation听说式
        //hr = m_cpRecoCtxt->CreateGrammar(GIDDICTATION, &m_cpDictationGrammar);
        //if (SUCCEEDED(hr))
        //{
        //  hr = m_cpDictationGrammar->LoadDictation(NULL, SPLO_STATIC);//加载词典
        //}

        //C&C命令式,此时语法文件使用xml格式
        ullGrammerID = 1000;
        hr = m_cpRecoCtxt->CreateGrammar(ullGrammerID, &m_cpCmdGrammar);

        WCHAR wszXMLFile[20] = L"";//加载语法
        MultiByteToWideChar(CP_ACP, 0, (LPCSTR)"CmdCtrl.xml", -1, wszXMLFile, 256);//ANSI转UNINCODE
        hr = m_cpCmdGrammar->LoadCmdFromFile(wszXMLFile, SPLO_DYNAMIC);


        //MessageBox(NULL, (LPCWSTR)L"语音识别已启动!", (LPCWSTR)L"提示", MB_CANCELTRYCONTINUE );
        //激活语法进行识别
        //hr = m_cpDictationGrammar->SetDictationState(SPRS_ACTIVE);//dictation
        hr = m_cpCmdGrammar->SetRuleState(NULL, NULL, SPRS_ACTIVE);//C&C
        hr = m_cpRecoEngine->SetRecoState(SPRST_ACTIVE);

    }

    else
    {
        MessageBox(NULL, (LPCWSTR)L"语音识别引擎启动出错!", (LPCWSTR)L"警告", MB_OK);
        exit(0);
    }


    //释放com资源
    ::CoUninitialize();
    //hr = m_cpCmdGrammar->SetRuleState(NULL, NULL, SPRS_INACTIVE);//C&C


}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

(5)定义消息处理函数 
需要和其他的消息处理代码放在一起,如本文代码中,放在文末示例代码的DlgProc()函数尾部。本文整个其他的代码块都可以直接照搬,只需要更改如下的消息反应模块即可

//消息处理函数
USES_CONVERSION;
    CSpEvent event;

    if (m_cpRecoCtxt)
    {
        while (event.GetFrom(m_cpRecoCtxt) == S_OK){

            switch (event.eEventId)
            {
            case SPEI_RECOGNITION:
            {
                                     //识别出了语音
                                     m_bGotReco = TRUE; 

                                     static const WCHAR wszUnrecognized[] = L"<Unrecognized>";

                                     CSpDynamicString dstrText;

                                     ////取得识别结果 
                                     if (FAILED(event.RecoResult()->GetText(SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, TRUE, &dstrText, NULL)))
                                     {
                                         dstrText = wszUnrecognized;
                                     }

                                     BSTR SRout;
                                     dstrText.CopyToBSTR(&SRout);
                                     CString Recstring;
                                     Recstring.Empty();
                                     Recstring = SRout;

                                    //做出反应(*****消息反应模块*****)
                                    if (Recstring == "发短信")
                                     {
                                         //MessageBox(NULL, (LPCWSTR)L"好的", (LPCWSTR)L"提示", MB_OK);
                                         MSSSpeak(LPCTSTR(_T("好,马上发短信!")));

                                     }

                                     else if (Recstring == "李雷")
                                     {
                                         MSSSpeak(LPCTSTR(_T("好久没看见他了,真是 long time no see")));
                                     }   

            }
                break;
            }
        }
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

(6)修改语法文件 
修改CmdCtrl.xml文件,可以提高某些词汇的识别度,对里面的词识别效果会很好多,如人名等。(此外,单独运行exe时也需要将此文件和exe放在同一文件夹内,不放也不会报错,只是语法文件里的词汇识别效果变差)

<?xml version="1.0" encoding="utf-8"?>
<GRAMMAR LANGID="804">
  <DEFINE>
    <ID NAME="VID_SubName1" VAL="4001"/>
    <ID NAME="VID_SubName2" VAL="4002"/>
    <ID NAME="VID_SubName3" VAL="4003"/>
    <ID NAME="VID_SubName4" VAL="4004"/>
    <ID NAME="VID_SubName5" VAL="4005"/>
    <ID NAME="VID_SubName6" VAL="4006"/>
    <ID NAME="VID_SubName7" VAL="4007"/>
    <ID NAME="VID_SubName8" VAL="4008"/>
    <ID NAME="VID_SubName9" VAL="4009"/>
    <ID NAME="VID_SubNameRule" VAL="3001"/>
    <ID NAME="VID_TopLevelRule" VAL="3000"/>
  </DEFINE>
  <RULE ID="VID_TopLevelRule" TOPLEVEL="ACTIVE">
    <O>
      <L>
        <P>我要</P>
        <P>运行</P>
        <P>执行</P>
      </L>
    </O>
    <RULEREF REFID="VID_SubNameRule" />
  </RULE>
  <RULE ID="VID_SubNameRule" >
    <L PROPID="VID_SubNameRule">
      <P VAL="VID_SubName1">发短信</P>
      <P VAL="VID_SubName2">是的</P>
      <P VAL="VID_SubName3">好的</P>
      <P VAL="VID_SubName4">不用</P>
      <P VAL="VID_SubName5">李雷</P>
      <P VAL="VID_SubName6">韩梅梅</P>
      <P VAL="VID_SubName7">中文界面</P>
      <P VAL="VID_SubName8">英文界面</P>
      <P VAL="VID_SubName9">English</P>

    </L>
  </RULE>
</GRAMMAR>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

源代码下载

注意,本代码是在原来的项目中截取出来的,但可以独立运行。 
Microsoft Speech SDK 安装包下载: 
http://download.csdn.net/detail/michaelliang12/9510691 
文中示例程序下载(之前下载分数太高,我已经重新上传了新版本,也解决了kincect20.lib报错的问题。由于自己经常在csdn上下东西,也需要积分,需要还是需要各位捧场,2积分。。): 
http://download.csdn.net/detail/michaelliang12/9766783

存在的bug:每次运行完程序,Windows的语音识别助手不会自动关闭,需要自己手动关闭。若不关闭,则下次启动程序可能会出错。大家如果有好的解决办法,请联系我,谢了!

参考网站

1,http://www.cnblogs.com/eping/archive/2010/05/23/1742201.html 
2,http://blog.csdn.net/pamchen/article/details/7856207 
3,http://blog.csdn.net/jmxiaocai/article/details/7036033 
4,http://blog.csdn.net/buaalei/article/details/5372544(主要参考) 
5,http://blog.csdn.net/itcastcpp/article/details/5313204 
6,http://blog.csdn.net/artemisrj/article/details/8723095(MFC的消息处理响应版本)

2017-12-13 13:57:38 fang_yang_wa 阅读数 3339
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5915 人正在学习 去看看 杨波

Python调用科大讯飞C语言接口实现语音识别,源代码:

from ctypes import *
import time
 
 # 调用动态链接库
dll = cdll.LoadLibrary("libmsc.so")
 #登录参数,apppid一定要和你的下载SDK对应
login_params = b"appid = 57738ed9, work_dir = ."
 
FRAME_LEN = 640  # Byte
 
MSP_SUCCESS = 0
# 返回结果状态
MSP_AUDIO_SAMPLE_FIRST = 1
MSP_AUDIO_SAMPLE_CONTINUE = 2
MSP_AUDIO_SAMPLE_LAST = 4
MSP_REC_STATUS_COMPLETE = 5
 # 你的语音文件路径
filename = "testAudio.wav"
 
class Msp:
    def __init__(self):
        pass
 
    def login(self):
        ret = dll.MSPLogin(None, None, login_params)
        # print('MSPLogin =>', ret)
 
    def logout(self):
        ret = dll.MSPLogout()
        # print('MSPLogout =>', ret)
 
    def isr(self, audiofile, session_begin_params):
        ret = c_int()
        sessionID = c_voidp()
        dll.QISRSessionBegin.restype = c_char_p
        sessionID = dll.QISRSessionBegin(None, session_begin_params, byref(ret))
        print('QISRSessionBegin => sessionID:', sessionID, 'ret:', ret.value)
 
        # 每秒【1000ms】  16000 次 * 16 bit 【20B】 ,每毫秒:1.6 * 16bit 【1.6*2B】 = 32Byte
        # 1帧音频20ms【640B】 每次写入 10帧=200ms 【6400B】
 
        #piceLne = FRAME_LEN * 20
        piceLne = 1638*2
        epStatus = c_int(0)
        recogStatus = c_int(0)
 
        wavFile = open(audiofile, 'rb')
        wavData = wavFile.read(piceLne)
 
        ret = dll.QISRAudioWrite(sessionID, wavData, len(wavData), MSP_AUDIO_SAMPLE_FIRST, byref(epStatus), byref(recogStatus))
        print('len(wavData):', len(wavData), 'QISRAudioWrite ret:', ret, 'epStatus:', epStatus.value, 'recogStatus:', recogStatus.value)
        time.sleep(0.1)
        while wavData:
            wavData = wavFile.read(piceLne)
 
            if len(wavData) == 0:
                break
 
            ret = dll.QISRAudioWrite(sessionID, wavData, len(wavData), MSP_AUDIO_SAMPLE_CONTINUE, byref(epStatus), byref(recogStatus))
            # print('len(wavData):', len(wavData), 'QISRAudioWrite ret:', ret, 'epStatus:', epStatus.value, 'recogStatus:', recogStatus.value)
            time.sleep(0.1)
        wavFile.close()
        ret = dll.QISRAudioWrite(sessionID, None, 0, MSP_AUDIO_SAMPLE_LAST, byref(epStatus), byref(recogStatus))
        # print('len(wavData):', len(wavData), 'QISRAudioWrite ret:', ret, 'epStatus:', epStatus.value, 'recogStatus:', recogStatus.value)
 
        print("所有待识别音频已全部发送完毕,等待获取识别结果")
 
        # -- 获取音频
        laststr = ''
        counter = 0
        while recogStatus.value != MSP_REC_STATUS_COMPLETE:
            ret = c_int()
            dll.QISRGetResult.restype = c_char_p
            retstr = dll.QISRGetResult(sessionID, byref(recogStatus), 0, byref(ret))
            if retstr is not None:
                laststr += retstr.decode()
                # print(laststr)
            # print('ret:', ret.value, 'recogStatus:', recogStatus.value)
            counter += 1
            time.sleep(0.2)
            counter += 1
            if counter == 500:
                laststr += '讯飞语音识别失败'
                break
 
        print(laststr)
        ret = dll.QISRSessionEnd(sessionID, '\0')
        # print('end ret: ', ret)
        return laststr
 
 
def XF_text(filepath, audiorate):
    msp = Msp()
    print("登录科大讯飞")
    msp.login()
    print("科大讯飞登录成功")
    session_begin_params = b"sub = iat, ptt = 0, result_encoding = utf8, result_type = plain, domain = iat"
    if 16000 == audiorate:
        session_begin_params = b"sub = iat, domain = iat, language = zh_cn, accent = mandarin, sample_rate = 16000, result_type = plain, result_encoding = utf8"
    text = msp.isr(filepath, session_begin_params)
    msp.logout()
    return text
 

if __name__ == '__main__':
    # 8000为音频码率
    res = XF_text(filename, 16000)
    #print(res)


2017-09-22 10:34:10 u013677156 阅读数 2047
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5915 人正在学习 去看看 杨波

kaldi源代码简单说明

kaldi是开源的,基于C++的语音识别工具。一方面语音识别有较高的技术门槛,包含了很多方面的东西,另一方面kaldi集成了太多的东西,造成了其代码量很大,阅读起来很困难。

kaldi现在集成了很多的东西,造成其代码量很大,直接阅读起来感觉无从下手。但是,每个项目,一开始的时候,都是比较简单的,代码量也较小。

我从git上下载过来kaldi源码后,通过git reset 命令,回退到最原始的版本,看最原始版本的代码,就没有那么难了。最原始的版本,虽然缺失了很多功能,比如online解码、神经网络,kws等,但是其基本的代码还是有的。这里主要就是记录了阅读这些代码时总结的一些东西。当然,代码也只是走马观花地看了一遍,没有仔细看细节。


kaldi/base 文件夹 ==============================================
  包含两个基本的头文件 kaldi-type.h  kaldi-common.h
  四个小模块 kaldi-error   kaldi-math   kaldi-utils   io-funcs
  kaldi-type.h 中定义了基本的unint32 int32等类型,以及BaseFloat
  kaildi-error 中,定义了 KALDI_ERR   KALDI_WARN  KALDI_LOG  和 KALDI_EXIT 等。还是用的define,以及类中返回stream的方法。比起后面的版本,要简单很多
  kalid-math 中,定义了基本的π,根号2等的值,定义一些最大最小值,定义随机数的实现,LogAdd LogSum等,公约数,因式分解等
  kaldi-utils中,KALDI_SWAP4  KALDI_DISALLOW_COPY_AND_ASSIGN   KALDI_ASSERT_IS_INTEGER_TYPE  KALDI_STRTOLL 等
  io-funcs中,定义基本输入输出,类模板定义方式 WriteBasicType  ReadBasicType,可以是二进制或文本模式
  四个小模块分别生成.o文件,然后生成kaildi-base.a。每个模块都有测试代码。

kaldi/matrix文件夹 ==============================================
  生成的.o文件 kaldi-matrix.o  kaldi-vector.o  packed-matrix.o  sp-matrix.o  tp-matrix.o  matrix-functions.o  srfft.o
  生成的.a文件 kaldi-matrix.a

  kaldi-blas.h matrix-comm.h  两个基本的头文件;前者定义使用哪个blas,后者定义了基本的矩阵类型

  packed-matrix 基本压缩矩阵,声明模板类PackedMatrix
  tp-matrix 和 sp-matrix  三角矩阵和对称矩阵TpMatrix SpMatrix,其基类是PackedMatrix
  kaldi-vector 和 kaldi-matrix   kaldi中的向量和矩阵,声明模板类 VectorBase Vector SubVector     MatrixBase SubMatrix Matrix
  jama-eig jama-svd 特征分解和奇异值分解,只有使用atlas时才用到这两个文件,因为其他的库已经自带这两个算法了

  matrix-functions 矩阵计算应用函数,如计算FFT; srfft 另一种高效的FFT计算方法
  matrix-lib.h 包含了所有的应用头文件

Eigenvalue Decomposition 特征分解
Singular Value Decomposition 奇异值分解
symmetric matrices 对称矩阵
invert 求逆;  singular matrix 奇异矩阵,行列式为0;非奇异矩阵才可以求逆矩阵
transpose 转置

kaldi/utils文件夹 ===============================================
  【common-utils.h】包含此文件夹下所有的应用头文件
  【timer.h】定义一个时间对象,可以计算当时与生成该对象时的时间差。
  【edit-distance】计算编辑距离
  【stl-utils】stl一部分STL算法函数总结,很多地方都会用到
  【const-integer-set】快速判断一个整数是否在一个集合中
  【hash-list】解码时用到的一个结构体/类

  生成五个.o文件:text-utils.o  kaldi-io.o  kaldi-table.o  parse-options.o  simple-io.funcs.o 最后生成 kaldi-utils.a
  1 text-utils 含一些基本的字符串处理函数,如SplitStringToVector  SplitStringToIntegers  ConvertStringToInteger
  2 parse-options 处理参数的类ParseOptions。用到text-utils
  3 kaldi-io 定义了 Output 和 Input 这两个类,用于输入输出。用到 text-utils/parse-optins/kaldi-pipebuf这些头文件
  4 simple-io-funcs 对kaldi-io又进行了简单封装,定义成了4个IO函数。
  5 kaldi-table 定义了一些table类,如 RandomAccessTableReader  SequentialTableReader
  【table-types】定义常用的tabel types。它依赖于 kaldi-table/kaldi-holder和matrix。需要认真搞懂的类。

kaldi中blas库的使用 ============================================
  1、kaldi默认使用 atlas,但可以在 src目录下运行 configure 时指定用其他的blas库,如openblas,mkl等
  2、用atlas时,configure代码会用多种方式检查系统中是否有atlas相关的动态/静态库了,如果有了,则只需atlas的头文件,就可以编译了。如果没有,则configure出错,需要先安装atlas
  3、使用openblas,则需要先手动安装好openblas。如果程序已经是多线程的了,则不要在安装openblas时,指定USE_THREAD=1。
  4、指定的blas库不同,生成的kaldi.mk文件不一样,这些不同会影响编译链接,影响src/matrix/kaldi-blas.h文件的define语句。
  5、一般编解码用的代码,可以在configure中指定用静态库。
  6、libquadmath.so 是高精度浮点数计算用的,GCC 4.6之后支持; libgfortran,一般编译blas库均用到

kaldi/itf文件夹 ================================================
  1、clusterable-itf:聚类接口
  2、context-dep-itf:联系tree和fst的接口,可以将一串上下文音素,映射为叶子ID。
  3、decodable-itf:解码接口,包含特征提取和声学模型,可供解码对象用
  4、optimizable-itf:好像是优化计算的接口,比如计算梯度。

kaldi/feat文件夹 ===============================================
  1、feature-functions:定义了一些参数类(如MelBanksOptions)和常用函数(如Dither、ExtractWindow),是mel-computations和feature-mfcc的桥梁。
  2、mel-computations:定义了计算mel系数(特征)的类,mel特征是计算 mfcc和plp特征的基础。
  3、feature-mfcc、feature-plp:计算mfcc特征和plp特征的类。这些(包含mel)都用到矩阵,都依赖于matrix
  4、wave-reader:读取wav文件的类。
  feature-functions.o feature-mfcc.o feature-plp.o mel-computations.o wave-reader.o ----> kaldi-feature.a

kaldi/tree文件夹 ===============================================
  1、clusterable-class、cluster-utils 聚类相关的类和算法。 build-tree时通过聚类生成问题集,然后构建树
  2、event-map 定义的是EventType 和 EventMap (CE/SE/TE)这些类,树tree的主体,就是一个 EventMap 对象。
  3、build-tree、build-tree-questions和build-tree-utils是跟构建树相关的类和函数,最终生成的是 EventMap 对象。
  4、context-dep 从接口ContextDependencyInterface派生出类ContextDependency,这个就是“树”了。它的私有数据成员就是N、P和EventMap。它可以通过compute函数,来输入一个(三音素和pdfclass-id),输出其pdf-id。(这就和CLG的ilalel有点关系了)

kaldi/decoder文件夹 ============================================
  1、decodable-am-diag-gmm:依赖于gmm/hmm/itf/transform等,从DecodableInterface中派生出DecodableAmDiagGmm、DecodableAmDiagGmmScaled等多个类
  2、decodable-am-sgmm:依赖于sgmm/hmm/itf等,从DecodableInterface中派生出DecodableAmSgmm、DecodableAmSgmmScaled等类。
  3、training-graph-compiler:依赖于hmm/fst/fstext等,定义类TrainingGraphCompiler。
  4、kaldi-decoder:依赖于fst等。定义类模板template<class Decodable, class Fst> class KaldiDecoder。
  前面三项会生成 .o 文件;然后一起(可能汇合第4个)构成 kaldi-decoder.a 文件
  simple-decoder.h 和 faster-decoder.h 定义两个类,但貌似这些代码不参与 .a 文件的生成。
  decodable-matrix.h 从DecodableInterface派生出DecodableMatrixScaled,其实就是在计算likelihood时乘以了scale。

kaldi/hmm文件夹 =============================================
  1、hmm-topology:定义类HmmTopology。这个描述了音素的HMM结构。
  2、transition-model:转移模型类TransitionModel。里面涉及了phone、HMM-state、pdf-id、transition-state、transition-index、transition-id这些概念。因为涉及了pdf-id,所以其依赖于ContextDependency(树tree)。
  3、hmm-utils:跟转移模型相关的函数或类,比如,将TransitionModel转为Ha.fst的函数,增加自环的函数等。需要依赖fst。
  4、tree-accu:只有一个函数,将状态的和加起来,用以训练树。

  transition-model中的三元组,由“音素/状态/ PDF”构成。这些三元组,就是transition-state。每个state中有若干转移,就是transition-index。所有t-state的index集合起来,就是t-id。TransitionModel中有函数,进行这些id直接的转换。

kaldi/gmm文件夹 =============================================
  按照文档说明,类DiagGmm是一个简单的对角协方差高斯混合模型。一个声学模型,就是由很多个DiagGmm构成的,比如AmDiagGmm。AmDiagGmm由pdf-id做索引,每一项都是一个DiagGmm。一个AmDiagGmm和一个TransitionModel结合起来,就是一个完整的模型(写入磁盘的,就是这两个的集合)。
  DiagGmm和AmDiagGmm都是功能简单的类,它们没有例如模型估计、特征参数变换等功能。
  FullGmm跟DiagGmm类似,但它是全协方差的GMM模型。它主要为训练UBMs做准备。
  【model-comm.h】【diag-gmm.h】【am-diag-gmm.h】【estimate-diag-gmm/estimate-am-diag-gmm.h】
                                【full-gmm.h】【estimate-full-gmm.h】

kaldi/gmmbin文件夹 ===========================================
  在前面feature、gmm、hmm、tree和decoder等的基础上,实现了一系列的命令行工具。如对于简单的gmm,就有如下一些:
  gmm-init-mono   mono训练的第一步,用以生成0.mdl和tree
  gmm-est   模型参数重新估计和更新,输入old.mdl和acc文件,输出new.mdl。基本就是AmDiagGmm和TransitionModel根据数据,执行Update函数。
  gmm-acc-stats-ali   根据对齐结果,计算数据,放入acc文件中。
  gmm-decode-simple  使用SimpleDecoder做解码。就是AmDiagGmm和TransitionModel和特征,构成decodable,然后读取fst,执行Decode()。
  gmm-decode-faster  使用FasterDecoder做解码。
  gmm-align-compiled  根据模型、图和特征,做对齐操作。其实就是读取AmDiagGmm和TransitionModel,然后在图上加上转移概率,然后做FasterDecoder。
  gmm-align 跟上面的gmm-align-compiled类似,只是这个输入没有图,只有L.fst和scp,需要自己编译图,然后做解码。
  gmm-sum-accs  将多个acc文件合并为一个。

kaldi/lm文件夹 ===============================================
  kaldi-lmtable和kaldi-lm 最终生成 kaldi-lm.a;它们的功能就是将arpa文件转为G.fst。
  arpa2fst.cc 应用的就是上面两个文件/类。

kaldi/optimization文件夹 ===============================================
  一些优化算法

kaldi/transform文件夹 ===============================================
  一些特征变换的类或函数

kaldi/bin文件夹
  一些常用工具,例如以下几个
  cluster-phones  对音素聚类,生成问题集。输入是phone-set和tree-stats(acc-tree-stats的输出),输出是questions.txt。
  compile-questions 对于cluster-phones生成的结果,进行问题去重,以及转换格式。
  build-tree  根据问题集和状态,生成树。
  compile-train-graphs  生成训练图
  compute-wer 计算错误率
  make-h-transducer  生成Ha.fst


总的来说, base、matrix和utils这三个文件夹中的代码是基础。这三个文件夹中的代码,所定义的函数和类,基本功能需要先弄清楚。

feat是特征提取相关的。gmm、hmm和tree是声学模型相关的。fst和decoder跟解码有关。

看代码的时候,还要结合kaldi官网的资料,和一些网上的中文资料,会更快速,更容易看懂函数的意图。


2014-05-19 10:37:22 u012809352 阅读数 2017
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5915 人正在学习 去看看 杨波

首先导入科大讯飞工具包

MainActivity源代码:

package com.example.viocedemo;


import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;


import com.iflytek.cloud.speech.RecognizerResult;
import com.iflytek.cloud.speech.SpeechConstant;
import com.iflytek.cloud.speech.SpeechError;
import com.iflytek.cloud.speech.SpeechListener;
import com.iflytek.cloud.speech.SpeechSynthesizer;
import com.iflytek.cloud.speech.SpeechUser;
import com.iflytek.cloud.speech.SynthesizerListener;
import com.iflytek.cloud.ui.RecognizerDialog;
import com.iflytek.cloud.ui.RecognizerDialogListener;


public class MainActivity extends Activity implements OnClickListener,
SynthesizerListener {
private EditText editText;
private Button button1;
private Button button2;
// 合成对象
private SpeechSynthesizer speechSynthesizer;
// 识别窗口
private RecognizerDialog recognizerDialog;


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);


// appid换成自己申请的
SpeechUser.getUser().login(MainActivity.this, null, null,
"appid=12345678", listener);


init();
setParam();


}


/**
* 初始化UI
*/
public void init() {
editText = (EditText) findViewById(R.id.editText1);
button1 = (Button) findViewById(R.id.button1);
button2 = (Button) findViewById(R.id.button2);
button1.setOnClickListener(this);
button2.setOnClickListener(this);
}


/**
* 初始化语音类别
*/
public void setParam() {
speechSynthesizer = SpeechSynthesizer.createSynthesizer(this);
speechSynthesizer.setParameter(SpeechConstant.VOICE_NAME, "xiaoyan");
speechSynthesizer.setParameter(SpeechConstant.SPEED, "50");
speechSynthesizer.setParameter(SpeechConstant.VOLUME, "50");
speechSynthesizer.setParameter(SpeechConstant.PITCH, "50");
}


/**
* 识别语音的弹出框
*/
public void setDialog() {
recognizerDialog = new RecognizerDialog(this);
recognizerDialog.setParameter(SpeechConstant.DOMAIN, "iat");
recognizerDialog.setParameter(SpeechConstant.SAMPLE_RATE, "16000");
editText.setText(null);
// 显示Dialog
recognizerDialog.setListener(dialogListener);
recognizerDialog.show();
}


/**
* 识别回调监听器
*/
private RecognizerDialogListener dialogListener = new RecognizerDialogListener() {
// 识别结果回调
@Override
public void onResult(RecognizerResult arg0, boolean arg1) {
// TODO Auto-generated method stub
String text = JsonParser.parseIatResult(arg0.getResultString());
editText.append(text);
editText.setSelection(editText.length());
}


// 识别结束回调
@Override
public void onError(SpeechError arg0) {
// TODO Auto-generated method stub


}
};


@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}


@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.button1:// 语音播放按钮
String text = editText.getText().toString();
speechSynthesizer.startSpeaking(text, this);
break;
case R.id.button2:// 语音识别
setDialog();
break;
default:
break;
}
}


// 缓冲进度回调通知
@Override
public void onBufferProgress(int arg0, int arg1, int arg2, String arg3) {
// TODO Auto-generated method stub


}


// 结束回调
@Override
public void onCompleted(SpeechError arg0) {
// TODO Auto-generated method stub

}


// 开始播放
@Override
public void onSpeakBegin() {
// TODO Auto-generated method stub
}


// 暂停播放
@Override
public void onSpeakPaused() {
// TODO Auto-generated method stub

}


// 播放进度
@Override
public void onSpeakProgress(int arg0, int arg1, int arg2) {
// TODO Auto-generated method stub

}


// 继续播放
@Override
public void onSpeakResumed() {
// TODO Auto-generated method stub

}


/**
* 通用回调接口
*/
private SpeechListener listener = new SpeechListener() {


// 消息回调
@Override
public void onEvent(int arg0, Bundle arg1) {
// TODO Auto-generated method stub


}


// 数据回调
@Override
public void onData(byte[] arg0) {
// TODO Auto-generated method stub


}


// 结束回调(没有错误)
@Override
public void onCompleted(SpeechError arg0) {
// TODO Auto-generated method stub

}
};


}


JsonParser类源代码:

package com.example.viocedemo;


import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONTokener;


import android.text.TextUtils;


//import com.iflytek.speech.ErrorCode;
//import com.iflytek.speech.SpeechError;
/**
 * 对云端返回的Json结果进行解析
 * @author iFlytek
 * @since 20131211
 */
public class JsonParser {

/**
* 听写结果的Json格式解析
* @param json
* @return
*/
public static String parseIatResult(String json) {
if(TextUtils.isEmpty(json))
return "";

StringBuffer ret = new StringBuffer();
try {
JSONTokener tokener = new JSONTokener(json);
JSONObject joResult = new JSONObject(tokener);


JSONArray words = joResult.getJSONArray("ws");
for (int i = 0; i < words.length(); i++) {
JSONArray items = words.getJSONObject(i).getJSONArray("cw");
JSONObject obj = items.getJSONObject(0);
ret.append(obj.getString("w"));
}
} catch (Exception e) {
e.printStackTrace();
} 
return ret.toString();
}

}


UI布局:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >


    <EditText
        android:id="@+id/editText1"
        android:layout_width="match_parent"
        android:layout_height="250dip"
        android:gravity="top"
        android:ems="10" >


        <requestFocus />
    </EditText>


    <Button
        android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="语音播放" />


    <Button
        android:id="@+id/button2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="语音听写" />


</LinearLayout>

源代码下载

声音识别

阅读数 963

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