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

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

    6094 人正在学习 去看看 杨波

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-08-18 16:55:17 qq_22642239 阅读数 1170
  • C++语音识别开篇

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

    6094 人正在学习 去看看 杨波

目录

一、安装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-09-22 10:34:10 u013677156 阅读数 2162
  • C++语音识别开篇

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

    6094 人正在学习 去看看 杨波

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官网的资料,和一些网上的中文资料,会更快速,更容易看懂函数的意图。


2019-06-28 16:31:05 qq_15181569 阅读数 3517
  • C++语音识别开篇

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

    6094 人正在学习 去看看 杨波

一、原理图
在这里插入图片描述
二、源代码
LD3320.c

#include "ld3320.h"
#include "delay.h"
#include "usart.h"	
#include "string.h"
u8 ld3320Data;
extern u8 nAsrStatus;

unsigned long nMp3StartPos=0;
unsigned long nMp3Size=0;
unsigned long nMp3Pos=0;
u8 bMp3Play=0;							//	用来记录播放MP3的状态
u8 nLD_Mode = LD_MODE_IDLE;		//	用来记录当前是在进行ASR识别还是在播放MP3
u8 ucRegVal;
u8 ucHighInt;
u8 ucLowInt;
u8 ucStatus;
u8 ucSPVol=15; // MAX=15 MIN=0		//	Speaker喇叭输出的音量



void ld3320_init()
{
	GPIO_InitTypeDef  GPIO_InitStructure;
 	EXTI_InitTypeDef EXTI_InitStructure;
 	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE);	 //使能PB,PE端口时钟
	
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
	
	
	GPIO_InitStructure.GPIO_Pin =GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_15;				 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 
	GPIO_Init(GPIOA, &GPIO_InitStructure);					
	GPIO_SetBits(GPIOA,GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_15);
	
	GPIO_InitStructure.GPIO_Pin =GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15 ;				 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 
	GPIO_Init(GPIOB, &GPIO_InitStructure);					
	GPIO_SetBits(GPIOB,GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_14|GPIO_Pin_15);
		
	
	LD3320_MD=0;  //选择并口
	LD3320_RST=1;	
	LD3320_A0=0;
	
	
	GPIO_InitStructure.GPIO_Pin =GPIO_Pin_11;				 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 		 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 
	GPIO_Init(GPIOB, &GPIO_InitStructure);		

  	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);	//使能复用功能时钟

    // 中断线以及中断初始化配置   下降沿触发
  	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource11);

  	EXTI_InitStructure.EXTI_Line=EXTI_Line11;	
  	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;	
  	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
  	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  	EXTI_Init(&EXTI_InitStructure);	 	//根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
	
	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;			//使能外部中断通道
  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;	//抢占优先级0
  	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;					//子优先级0 
  	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;								//使能外部中断通道
  	NVIC_Init(&NVIC_InitStructure);  	  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器	

}

//外部中断服务程序
void EXTI15_10_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line11)!= RESET ) 
	{
		ld3320_process_init();		
		EXTI_ClearFlag(EXTI_Line11);
		EXTI_ClearITPendingBit(EXTI_Line11);//清除LINE上的中断标志位  
	} 	
}


void ld3320_write_reg( unsigned char address, unsigned char dataout )
{
	
	ld3320Data=address;
	LD3320_D0=bita0;LD3320_D1=bita1;LD3320_D2=bita2;LD3320_D3=bita3;
	LD3320_D4=bita4;LD3320_D5=bita5;LD3320_D6=bita6;LD3320_D7=bita7;
	delay_us(1);
	LD3320_A0  =1;
	LD3320_CS = 0;
	LD3320_WR = 0;

	LD3320_WR = 1;
	LD3320_CS = 1;
	

	
	ld3320Data=dataout;
	LD3320_D0=bita0;LD3320_D1=bita1;LD3320_D2=bita2;LD3320_D3=bita3;
	LD3320_D4=bita4;LD3320_D5=bita5;LD3320_D6=bita6;LD3320_D7=bita7;
	delay_us(1);
	LD3320_A0 = 0;
	LD3320_CS = 0;
	LD3320_WR = 0;

	LD3320_WR = 1;
	LD3320_CS = 1;
}

unsigned char ld3320_read_reg( unsigned char address )
{
	unsigned char datain;

	ld3320Data=address;
	LD3320_D0=bita0;LD3320_D1=bita1;LD3320_D2=bita2;LD3320_D3=bita3;
	LD3320_D4=bita4;LD3320_D5=bita5;LD3320_D6=bita6;LD3320_D7=bita7;
	
	LD3320_A0 = 1;
	delay_us(1);
	LD3320_CS = 0;
	delay_us(1);
	LD3320_WR = 0;
	delay_us(1);

	LD3320_WR = 1;
	delay_us(1);
	LD3320_CS = 1;
	delay_us(1);

	LD3320_A0 = 0;
	delay_us(1);
	LD3320_CS = 0;
	delay_us(1);
	LD3320_RD = 0;
	LD3320_DATA_IN();
	delay_us(1);
	
	bita0=LD3320_D0_R;bita1=LD3320_D1_R;bita2=LD3320_D2_R;bita3=LD3320_D3_R;
	bita4=LD3320_D4_R;bita5=LD3320_D5_R; bita6=LD3320_D6_R;bita7=LD3320_D7_R;
	datain= ld3320Data;
	
	LD3320_DATA_OUT();
	LD3320_RD = 1;
	delay_us(1);
	LD3320_CS = 1;

	return datain;
}


void ld3320_reset()
{
	LD3320_RST=1;
	delay_ms(1);
	LD3320_RST=0;
	delay_ms(1);
	LD3320_RST=1;

	delay_ms(1);
	LD3320_CS=0;
	delay_ms(1);
	LD3320_CS=1;
	delay_ms(1);
}
u8 ld3320_check()
{
	u8 a[3];
	memset(a,0x00,sizeof(a));
	ld3320_reset();
	delay_ms(1);
	ld3320_read_reg(0x06);  
	delay_ms(1);
	ld3320_write_reg(0x35, 0x33);  
	delay_ms(1);
	ld3320_write_reg(0x1b, 0x55);   
	delay_ms(1);
	ld3320_write_reg(0xb3, 0xaa);	
	
	a[0]=ld3320_read_reg(0x35);
	delay_ms(5);
		
	a[1]=ld3320_read_reg(0x1b);
	delay_ms(5);
	a[2]=ld3320_read_reg(0xb3);

	if(a[0]!=0x33||a[1]!=0x55||a[2]!=0xaa)
		return 1;
	return 0;
	
}



void ld3320_init_common()
{
	bMp3Play = 0;

	ld3320_read_reg(0x06);  
	ld3320_write_reg(0x17, 0x35); 
	delay_ms(10);
	ld3320_read_reg(0x06);  

	ld3320_write_reg(0x89, 0x03);  
	delay_ms(5);
	ld3320_write_reg(0xCF, 0x43);   
	delay_ms(5);
	ld3320_write_reg(0xCB, 0x02);
	
	/*PLL setting*/
	ld3320_write_reg(0x11, LD_PLL_11);       
	if (nLD_Mode == LD_MODE_MP3)
	{
		ld3320_write_reg(0x1E, 0x00); 
		ld3320_write_reg(0x19, LD_PLL_MP3_19);   
		ld3320_write_reg(0x1B, LD_PLL_MP3_1B);   
		ld3320_write_reg(0x1D, LD_PLL_MP3_1D);
	}
	else
	{
		ld3320_write_reg(0x1E,0x00);
		ld3320_write_reg(0x19, LD_PLL_ASR_19); 
		ld3320_write_reg(0x1B, LD_PLL_ASR_1B);		
	    ld3320_write_reg(0x1D, LD_PLL_ASR_1D);
	}
	delay_ms(10);
	
	ld3320_write_reg(0xCD, 0x04);
	ld3320_write_reg(0x17, 0x4c); 
	delay_ms(5);
	ld3320_write_reg(0xB9, 0x00);
	ld3320_write_reg(0xCF, 0x4F); 
	ld3320_write_reg(0x6F, 0xFF); 
}

void ld3320_init_mp3()
{
	nLD_Mode = LD_MODE_MP3;
	ld3320_init_common();

	ld3320_write_reg(0xBD,0x02);
	ld3320_write_reg(0x17, 0x48);
	delay_ms(10);

	ld3320_write_reg(0x85, 0x52); 
	ld3320_write_reg(0x8F, 0x00);  
	ld3320_write_reg(0x81, 0x00);
	ld3320_write_reg(0x83, 0x00);
	ld3320_write_reg(0x8E, 0xff);
	ld3320_write_reg(0x8D, 0xff);
    delay_ms(1);
	ld3320_write_reg(0x87, 0xff);
	ld3320_write_reg(0x89, 0xff);
	delay_ms(1);
	ld3320_write_reg(0x22, 0x00);    
	ld3320_write_reg(0x23, 0x00);
	ld3320_write_reg(0x20, 0xef);    
	ld3320_write_reg(0x21, 0x07);
	ld3320_write_reg(0x24, 0x77);          
    ld3320_write_reg(0x25, 0x03);
    ld3320_write_reg(0x26, 0xbb);    
    ld3320_write_reg(0x27, 0x01); 
}



void ld3320_init_asr()
{
	nLD_Mode=LD_MODE_ASR_RUN;
	ld3320_init_common();

	ld3320_write_reg(0xBD, 0x00);
	ld3320_write_reg(0x17, 0x48);
	delay_ms(10);

	ld3320_write_reg(0x3C, 0x80);    
	ld3320_write_reg(0x3E, 0x07);
	ld3320_write_reg(0x38, 0xff);    
	ld3320_write_reg(0x3A, 0x07);
	
	ld3320_write_reg(0x40, 0);          
	ld3320_write_reg(0x42, 8);
	ld3320_write_reg(0x44, 0);    
	ld3320_write_reg(0x46, 8); 
	delay_ms(10);
}

void ld3320_reload_mp3data()
{
	unsigned long nCurMp3Pos;
	u8 val;
	u8 k;

	nCurMp3Pos = nMp3StartPos + nMp3Pos;

	ucStatus = ld3320_read_reg(0x06);
	while ( !(ucStatus&MASK_FIFO_STATUS_AFULL) && (nMp3Pos<nMp3Size) )
	{
		val=0;
		for(k=0;k<8;k++)
		{
		}
		ld3320_write_reg(0x01,val);

		nMp3Pos++;

		ucStatus = ld3320_read_reg(0x06);
	}
}


void ld3320_reload_mp3data_2()
{
	unsigned long nCurMp3Pos;
	u8 val;
	u8 k;

	nCurMp3Pos = nMp3StartPos + nMp3Pos;


	//读取音乐文件写入
	
	

	ucStatus = ld3320_read_reg(0x06);
	while ( !(ucStatus&MASK_FIFO_STATUS_AFULL) && (nMp3Pos<nMp3Size) )
	{
		val=0;


		nMp3Pos++;

		ucStatus = ld3320_read_reg(0x06);
	}
}




void ld3320_process_init()
{
	u8 nAsrResCount=0;
	
	ucRegVal = ld3320_read_reg(0x2B);
	if(nLD_Mode == LD_MODE_ASR_RUN)
	{
		// 语音识别产生的中断
		// (有声音输入,不论识别成功或失败都有中断)
		ld3320_write_reg(0x29,0) ;
		ld3320_write_reg(0x02,0) ;
		if((ucRegVal & 0x10) &&
			ld3320_read_reg(0xb2)==0x21 && 
			ld3320_read_reg(0xbf)==0x35)
		{
			nAsrResCount = ld3320_read_reg(0xba);
			if(nAsrResCount>0 && nAsrResCount<=4) 
			{
				nAsrStatus=LD_ASR_FOUNDOK;
			}
			else
		    {
				nAsrStatus=LD_ASR_FOUNDZERO;
			}	
		}
		else
		{
			nAsrStatus=LD_ASR_FOUNDZERO;
		}
			
		ld3320_write_reg(0x2b, 0);
    	ld3320_write_reg(0x1C,0);
		return;
	}
	
	// 声音播放产生的中断,有三种:
	// A. 声音数据已全部播放完。
	// B. 声音数据已发送完毕。
	// C. 声音数据暂时将要用完,需要放入新的数据。	
	ucHighInt = ld3320_read_reg(0x29); 
	ucLowInt=ld3320_read_reg(0x02); 
	ld3320_write_reg(0x29,0) ;
	ld3320_write_reg(0x02,0) ;
    if(ld3320_read_reg(0xBA)&CAUSE_MP3_SONG_END)
    {
	// A. 声音数据已全部播放完。

		ld3320_write_reg(0x2B,  0);
      	ld3320_write_reg(0xBA, 0);	
		ld3320_write_reg(0xBC,0x0);	
		bMp3Play=0;					// 声音数据全部播放完后,修改bMp3Play的变量
		ld3320_write_reg(0x08,1);
		delay_ms(5);
      	ld3320_write_reg(0x08,0);
		ld3320_write_reg(0x33, 0);

		return ;
     }

	 if(nMp3Pos>=nMp3Size)
	{
	// B. 声音数据已发送完毕。

		ld3320_write_reg(0xBC, 0x01);
		ld3320_write_reg(0x29, 0x10);
//		bMp3Play=0;				//	此时,只是主控MCU把所有MP3数据发送到LD3320芯片内,但是还没有把送入的数据全部播放完毕
		return;	
	}

	// C. 声音数据暂时将要用完,需要放入新的数据。	

	ld3320_reload_mp3data_2();
		
	ld3320_write_reg(0x29,ucHighInt); 
	ld3320_write_reg(0x02,ucLowInt) ;
}
void ld3320_play()
{
	nMp3Pos=0;
	bMp3Play=1;

	if (nMp3Pos >=  nMp3Size)
		return ; 

	ld3320_reload_mp3data();

    ld3320_write_reg(0xBA, 0x00);
	ld3320_write_reg(0x17, 0x48);
	ld3320_write_reg(0x33, 0x01);
	ld3320_write_reg(0x29, 0x04);
	
	ld3320_write_reg(0x02, 0x01); 
	ld3320_write_reg(0x85, 0x5A);
}

void ld3320_adjust_volume(u8 val)
{
	ucSPVol = val;
	val = ((15-val)&0x0f) << 2;
	ld3320_write_reg(0x8E, val | 0xc3); 
	ld3320_write_reg(0x87, 0x78); 
}
// Return 1: success.
u8 ld3320_check_asrbusyflag_b2()
{
	u8 j;
	u8 flag = 0;
	for (j=0; j<10; j++)
	{
		if (ld3320_read_reg(0xb2) == 0x21)
		{
			flag = 1;
			break;
		}
		delay_ms(10);		
	}
	return flag;
}

void LD_AsrStart()
{
	ld3320_init_asr();
}

// Return 1: success.
u8 ld3320_asrun()
{
	ld3320_write_reg(0x35, MIC_VOL);
	ld3320_write_reg(0x1C, 0x09);
	ld3320_write_reg(0xBD, 0x20);
	ld3320_write_reg(0x08, 0x01);
	delay_ms( 1 );
	ld3320_write_reg(0x08, 0x00);
	delay_ms( 1 );

	if(ld3320_check_asrbusyflag_b2() == 0)
	{
		return 0;
	}

	ld3320_write_reg(0xB2, 0xff);	
	ld3320_write_reg(0x37, 0x06);
	delay_ms( 10 );
	ld3320_write_reg(0x1C, 0x0b);
	ld3320_write_reg(0x29, 0x10);
	
	ld3320_write_reg(0xBD, 0x00);
	return 1;
}

// Return 1: success.
//	添加识别关键词语,开发者可以学习"语音识别芯片LD3320高阶秘籍.pdf"中关于垃圾词语吸收错误的用法
u8 ld3320_asr_addFixed()
{
	u8 k, flag;
	u8 nAsrAddLength;
	const char sRecog[24][30] = {"xiao hui hui",
								"ni you xi fu mei",
								"jiang ge gu shi",
								"ni shi bu shi dan teng",
								"ni ban ben hao duo shao",
								"jin tian duo shao hao",
								"jin tian ji hao le",
								"xian zai ji dian le",
								"jin nian ji sui le",
								"ni sheng wa le mei",
								"da kai ji dian qi yi",
								"da kai ji dian qi er",
								"guan bi ji dian qi yi",
								"guan bi ji dian qi er",
								"da kai suo you ji dian qi",
								"guan bi suo you ji dian qi",
								"tiao gao yin liang",
								"zeng jia tin liang",
								"tiao xiao yin liang",
								"jiang di yin lliang",
								"tiao gao yu su",
								"zeng jia yu su",
								"tiao xiao yu su",
								"jiang di yu su",
								};
	const Order pCode[24] = {
							name,
							wife,
							speakStore,
							eggPain,
							visionNum,
							todayDate,
							whyToday,
							whatTime,
							howOld,
							haveChild,
							openRelayOne,
							openRelayTwo,
							closeRelayOne,
							closeRelayTwo,
							openRelay,
							closeRelay,
							upVolume,
							upVolume,
							downVolume,
							downVolume,
							upSpeed,
							upSpeed,
							downSpeed,
							downSpeed,
							};
		
	flag = 1;
	for (k=0; k<24; k++)
	{
			
		if(ld3320_check_asrbusyflag_b2() == 0)
		{
			flag = 0;
			break;
		}
		
		ld3320_write_reg(0xc1, pCode[k] );
		ld3320_write_reg(0xc3, 0 );
		ld3320_write_reg(0x08, 0x04);
		delay_ms(1);
		ld3320_write_reg(0x08, 0x00);
		delay_ms(1);

		for (nAsrAddLength=0; nAsrAddLength<30; nAsrAddLength++)
		{
			if (sRecog[k][nAsrAddLength] == 0)
				break;
			ld3320_write_reg(0x5, sRecog[k][nAsrAddLength]);
		}
		ld3320_write_reg(0xb9, nAsrAddLength);
		ld3320_write_reg(0xb2, 0xff);
		ld3320_write_reg(0x37, 0x04);
	}
    return flag;
}


u8 ld3320_get_result()
{
	return ld3320_read_reg(0xc5 );
}

/************************************************************************************/
//	RunASR()函数实现了一次完整的ASR语音识别流程
//	LD_AsrStart() 函数实现了ASR初始化
//	LD_AsrAddFixed() 函数实现了添加关键词语到LD3320芯片中
//	LD_AsrRun()	函数启动了一次ASR语音识别流程
//
//	任何一次ASR识别流程,都需要按照这个顺序,从初始化开始进行
/************************************************************************************/

u8 ld3320_run_asr()
{
	u8 i=0;
	u8 asrflag=0;
	for (i=0; i<5; i++)			//	防止由于硬件原因导致LD3320芯片工作不正常,所以一共尝试5次启动ASR识别流程
	{
		LD_AsrStart();
		delay_ms(10);
		if (ld3320_asr_addFixed()==0)
		{
			ld3320_reset();			//	LD3320芯片内部出现不正常,立即重启LD3320芯片
			delay_ms(100);			//	并从初始化开始重新ASR识别流程
			continue;
		}
		delay_ms(300);
		if (ld3320_asrun() == 0)
		{
			ld3320_reset();			//	LD3320芯片内部出现不正常,立即重启LD3320芯片
			delay_ms(100);			//	并从初始化开始重新ASR识别流程
			continue;
		}

		asrflag=1;
		break;					//	ASR流程启动成功,退出当前for循环。开始等待LD3320送出的中断信号
	}

	return asrflag;
}

LD3320.h

#ifndef __LD3320_H 
#define __LD3320_H 
#include "sys.h"

typedef struct
{
    unsigned char  b0:1; 
    unsigned char  b1:1; 
    unsigned char  b2:1; 
    unsigned char  b3:1; 
    unsigned char  b4:1;
    unsigned char  b5:1;
    unsigned char  b6:1;
    unsigned char  b7:1;
} BIT8;

extern u8 ld3320Data;
#define     bita0               (((BIT8*) & ld3320Data) -> b0)
#define     bita1               (((BIT8*) & ld3320Data) -> b1)
#define     bita2               (((BIT8*) & ld3320Data) -> b2)
#define     bita3               (((BIT8*) & ld3320Data) -> b3)
#define     bita4               (((BIT8*) & ld3320Data) -> b4)
#define     bita5               (((BIT8*) & ld3320Data) -> b5)
#define     bita6               (((BIT8*) & ld3320Data) -> b6)
#define     bita7               (((BIT8*) & ld3320Data) -> b7)




#define LD3320_A0   PBout(15)
#define LD3320_RD   PBout(14)
#define LD3320_WR   PAout(12)
#define LD3320_CS   PAout(11)
#define LD3320_RST  PBout(10)
#define LD3320_MD   PBout(13)
		
#define LD3320_D0 PBout(5)
#define LD3320_D1 PBout(4)
#define LD3320_D2 PBout(3)
#define LD3320_D3 PAout(15)
#define LD3320_D4 PBout(9)
#define LD3320_D5 PBout(8)
#define LD3320_D6 PBout(7)
#define LD3320_D7 PBout(6)

#define LD3320_D0_R PBin(5)
#define LD3320_D1_R PBin(4)
#define LD3320_D2_R PBin(3)
#define LD3320_D3_R PAin(15)
#define LD3320_D4_R PBin(9)
#define LD3320_D5_R PBin(8)
#define LD3320_D6_R PBin(7)
#define LD3320_D7_R PBin(6)


#define LD3320_DATA_IN()  {GPIOA->CRH&=0X0FFFFFFF;GPIOA->CRH|=(u32)8<<28;GPIOB->CRL&=0X00000FFF;GPIOB->CRL|=0X88888000;GPIOB->CRH&=0XFFFFFF00;GPIOB->CRH|=0X00000088;}
#define LD3320_DATA_OUT() {GPIOA->CRH&=0X0FFFFFFF;GPIOA->CRH|=(u32)3<<28;GPIOB->CRL&=0X00000FFF;GPIOB->CRL|=0X33333000;GPIOB->CRH&=0XFFFFFF00;GPIOB->CRH|=0X00000033;}


#define LD3320_DATA_WRITE(x) {ld3320Data = x;LD3320_D0=bita0;LD3320_D1=bita1;LD3320_D2=bita2;LD3320_D3=bita3;LD3320_D4=bita4;LD3320_D5=bita5;LD3320_D6=bita6;LD3320_D7=bita7;}
#define LD3320_DATA_READ(x)  {bita0=LD3320_D0_R;bita1=LD3320_D1_R;bita2=LD3320_D2_R;bita3=LD3320_D3_R;bita4=LD3320_D4_R;bita5=LD3320_D5_R; bita6=LD3320_D6_R;bita7=LD3320_D7_R;x= ld3320Data;}

	
	
//	以下三个状态定义用来记录程序是在运行ASR识别还是在运行MP3播放
#define LD_MODE_IDLE		0x00
#define LD_MODE_ASR_RUN		0x08
#define LD_MODE_MP3		 	0x40


//	以下五个状态定义用来记录程序是在运行ASR识别过程中的哪个状态
#define LD_ASR_NONE				0x00	//	表示没有在作ASR识别
#define LD_ASR_RUNING			0x01	//	表示LD3320正在作ASR识别中
#define LD_ASR_FOUNDOK			0x10	//	表示一次识别流程结束后,有一个识别结果
#define LD_ASR_FOUNDZERO 		0x11	//	表示一次识别流程结束后,没有识别结果
#define LD_ASR_ERROR	 		0x31	//	表示一次识别流程中LD3320芯片内部出现不正确的状态


#define CLK_IN   		    8	/* user need modify this value according to clock in */
#define LD_PLL_11			(u8)((CLK_IN/2.0)-1)
#define LD_PLL_MP3_19		0x0f
#define LD_PLL_MP3_1B		0x18
#define LD_PLL_MP3_1D   	(u8)(((90.0*((LD_PLL_11)+1))/(CLK_IN))-1)

#define LD_PLL_ASR_19 		(u8)(CLK_IN*32.0/(LD_PLL_11+1) - 0.51)
#define LD_PLL_ASR_1B 		0x48
#define LD_PLL_ASR_1D 		0x1f

// LD chip fixed values.
#define        RESUM_OF_MUSIC               0x01
#define        CAUSE_MP3_SONG_END           0x20

#define        MASK_INT_SYNC				0x10
#define        MASK_INT_FIFO				0x04
#define    	   MASK_AFIFO_INT				0x01
#define        MASK_FIFO_STATUS_AFULL		0x08	
	
	
	
typedef enum
{
	name=1,  //小灰灰 
	wife=2,  //你有媳妇没
	speakStore=3,//讲个故事
	eggPain=4,  //你最近是不是蛋疼
	visionNum=5, //你版本号是多少
	todayDate=6,//今天多少号
	whyToday=6, //今天几号了
	whatTime=7,  //现在几点了
	howOld=8,    //今年几岁了
	haveChild=9, //你生娃了没
	openRelayOne=10, //打开继电器一
	openRelayTwo=11,  //打开继电器二
	closeRelayOne=12, //关闭继电器一
	closeRelayTwo=13, //关闭继电器二
	openRelay=14,     //打开所有继电器
	closeRelay=15,    //关闭所有继电器
	upVolume=17,   //调高音量
	downVolume=18, //降低音量
	upSpeed=19,		//提高语速
	downSpeed=20,      //降低语速
}Order;
	
	
	
	
	
extern unsigned long nMp3StartPos;
extern unsigned long nMp3Size;
extern unsigned long nMp3Pos;
extern u8 bMp3Play;							//	用来记录播放MP3的状态
extern u8 nLD_Mode;		//	用来记录当前是在进行ASR识别还是在播放MP3
extern u8 ucRegVal;
extern u8 ucHighInt;
extern u8 ucLowInt;
extern u8 ucStatus;
extern u8 ucSPVol; // MAX=15 MIN=0		//	Speaker喇叭输出的音量
#define MIC_VOL 0x55	
	
		
u8 ld3320_check(void);	
void ld3320_init(void);	
void ld3320_process_init(void);	
void ld3320_reset(void);
void ld3320_init_common(void);
void ld3320_init_mp3(void);
void ld3320_init_asr(void);
void ld3320_reload_mp3data(void);
void ld3320_reload_mp3data_2(void);
void ld3320_play(void);
void ld3320_adjust_volume(u8 val);
u8 ld3320_check_asrbusyflag_b2(void);
void LD_AsrStart(void);
u8 ld3320_asrun(void);
u8 ld3320_asr_addFixed(void);
u8 ld3320_get_result(void);
u8 ld3320_run_asr(void);
	
#endif

freq.c

#include "freq.h"


void ld3320_freq_init(void)
{
 
	GPIO_InitTypeDef  GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);	 //使能PB,PE端口时钟


	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);	
	GPIO_SetBits(GPIOA,GPIO_Pin_8);
	
	RCC_MCOConfig(RCC_MCO_HSE); //RCC_MCO_NoClock --- 无时钟输出 
								//RCC_MCO_SYSCLK --- 输出系统时钟(SysCLK) 
								//RCC_MCO_HSI --- 输出内部高速8MHz的RC振荡器的时钟(HSI) 
								//RCC_MCO_HSE --- 输出高速外部时钟信号(HSE) 
								//RCC_MCO_PLLCLK_Div2 --- 输出PLL倍频后的二分频时钟(PLLCLK/2)		
}
 

freq.h

#ifndef __FREQ_H
#define __FREQ_H 
#include "sys.h"


void ld3320_freq_init(void);	

		 				    
#endif

main.c

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "ld3320.h"
#include "freq.h"
#include "malloc.h"	

 
u8 nAsrStatus=0;	
extern u8 bMp3Play;							//	用来记录播放MP3的状态

u8 voiceSpeed=5,voiceVolume=10;

int main(void)
{	
	u8  nAsrRes;
	u16 i;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	uart_init(115200);
	
	delay_init();	
	ld3320_freq_init();
	led_init();	
	mem_init();				//初始化内存池
	ld3320_init();
	delay_ms(1000);
	ld3320_reset();
	while(ld3320_check())
	{
		printf("LD3320 Error!!\r\n");
		turn_r_led();
		delay_ms(500);
	}	
	nAsrStatus = LD_ASR_NONE;		//	初始状态:没有在作ASR
		
	while(1)
	{	
		if (bMp3Play!=0)			//	如果还在播放MP3中,则等待,直到MP3播放完毕 即bMp3Play==0
			continue;				//	bMp3Play 是定义的一个全局变量用来记录MP3播放的状态,不是LD3320芯片内部的寄存器
		switch(nAsrStatus)
		{
			case LD_ASR_RUNING:	break;
			case LD_ASR_ERROR:	break;
				
			case LD_ASR_NONE:
			{	
				nAsrStatus=LD_ASR_RUNING;
				if (ld3320_run_asr()==0)	
				{
					nAsrStatus = LD_ASR_ERROR;
				}
				
			}break;
			case LD_ASR_FOUNDOK:
			{
				nAsrRes = ld3320_get_result();
				printf("nAsrRes=%d\r\n",nAsrRes);				
				switch(nAsrRes)
				{
					case name:
					{           
						printf("小灰灰\r\n");	
					}break;
					case wife:    
					{
						printf("你有媳妇没\r\n");
					}break;
					case speakStore: 
					{			
						printf("讲个故事\r\n");
					}break;
					case eggPain:    
					{						
						printf("你最近是不是蛋疼\r\n");
					}break;
					case visionNum:   
					{						
						printf("你版本号是多少\r\n");
					}break;
					case todayDate: 
					{					
						printf("今天多少号\r\n");
					}break;
					case whatTime:  
					{						
						printf("现在几点了\r\n");					
					}break;
					case howOld:        
					{						
						printf("今年几岁了\r\n");
					}break;
					case haveChild:  
					{					
						printf("你生娃了没\r\n");
					}break;
					case openRelayOne: 
					{					
						printf("打开继电器一\r\n");
					}break;
					case openRelayTwo:
					{						
						printf("打开继电器二\r\n");
					}break;
					case closeRelayOne: 
					{						
						printf("关闭继电器一\r\n");
					}break;
					case closeRelayTwo: 
					{						
						printf("关闭继电器二\r\n");
					}break;
					case openRelay:    
					{					
						printf("打开所有继电器\r\n");
					}break;
					case closeRelay:  
					{						
						printf("关闭所有继电器\r\n");
					}break;	
					case upVolume:   
					{						
						printf("调高音量\r\n");
					}break;	
					case downVolume: 
					{    
						printf("降低音量\r\n");
					}break;	
					case upSpeed:
					{					
						printf("提高语速\r\n");
					}break;	
					case downSpeed:  
					{					
						printf("降低语速\r\n");
						
					}break;					
				}
				nAsrStatus = LD_ASR_NONE;
				
			}break;
			case LD_ASR_FOUNDZERO:
			default:
			{
				nAsrStatus = LD_ASR_NONE;
				
			}break;
		}
		delay_us(2);
		i++;
		if(i>=60000)
		{
			i=0;
			turn_g_led();
		}
	}
}

完整资料下载:https://download.csdn.net/download/qq_15181569/11263439

2019-05-28 14:24:53 qq_33472146 阅读数 226
  • C++语音识别开篇

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

    6094 人正在学习 去看看 杨波

一 HTK简介
HTK(HMM Tools Kit)是一个剑桥大学开发的专门用于建立和处理HMM的实验工具包[1],主要应用于语音识别领域,也可以应用于语音合成、字符识别和DNA排序等领域。HTK经过剑桥大学、Entropic公司及Microsoft公司的不断增强和改进,使其在语音识别领域处于世界领先水平,另外,HTK还是一套源代码开放的工具箱,其基于ANSI C的模块化设计方式可以方便地嵌入到用户系统中。
二 HTK的使用方法
HTK目录结构
htk是一个开源的软件,解压之后的目录如下:
在这里插入图片描述
此外,几个重要的文件说明如下:
env:编译的环境变量设置。
HLMLib:HMM语言模型库。
HLMTools:HMM语言模型工具。
HTKBook:开发帮助文档。
HTKLib:HTK的声学库文件。
HTKTools:HTK的声学开发工具。
对于声学部分来说,比较重要两个文件夹是后面的两个文件夹。
HTK原理
HTK软件的体系结构如下图所示:
在这里插入图片描述
整个HTK是使用HMM作为语音识别的核心,当HMM应用于孤立词语音识别时,它用不同的隐含状态来描述不同的语音发音,对于连续语音识别系统,多个孤立词HMM子模型按一定的语言模型组成的复合HMM模型序列来刻画连续的语音信号,在序列中每个模型直接对应于相关的发音,并且每一个模型都有进入和退出状态,这两个状态没有对应的观察矢量,只用于不同模型的连接。

上图中的 HTK Tool周边的一些库都是HTKLib,这些库是语音信号处理时必须的资源或者通用库文件,HTK Tool正是利用HTKLib文件夹中的库文件(如HMem,HSigP等库文件),链接生成一些可执行程序。如HSigP库完成信号的预加重,信号加窗,FFT等方面的计算。
HTK工具使用
HTK工具的使用主要分布在使用过程中的四个阶段:
1.数据准备;
2.模型训练;
3.模式识别/结果测试;
4.模型分析
如图所示:
在这里插入图片描述
每一个HTK命令行工具都在特定的阶段完成特定的任务。整个入口就是原始的声音文件和对声音文件的文本记录信息。
恩,后续再补。。。

csdn的开始

阅读数 169

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