zoukankan      html  css  js  c++  java
  • QT中使用微软Speech API实现语音识别

    转载:http://billxia.diandian.com/post/2012-12-23/40049402032

    在Windows下,使用Microsoft Speech API(简称为SAPI)可以很简单高效的实现语音识别,关于如何使用SAPI实现语音识别的文章请参见MVP尹成的博客 : 

    微软语音识别语音朗读技术

    VC++基于微软语音引擎开发语音识别总结


    而Speech SDK安装后有一个Samples文件夹,里面有C++/C#/VB的示例代码可以参考。 
    现在我想把基于SAPI的语音识别转移到我的QT的项目里,也就是在QT里调用微软的SAPI来实现语音识别。这个想法是很简单的,但要实现的话,却充满了阻碍。我首先查了一下QT调用Win API的可能性,发现这是完全可以的;接下来就着手来实现了。 
    首次还是封装一个Speech Recognition的引擎类SREngine,其头文件和源文件分别如下: 

    /****************************************************
       SREngine类,将MS SAPI的语音识别引擎封装,用于语音识别
    SREngine.h
    ****************************************************/
    #ifndef SRENGINE_H
    #define SRENGINE_H
    
    
    
    #include <QString.h>
    #include <QMessageBox>
    
    
    
    // Microsoft Speech API
    #undef UNICODE
    #include <sapi.h>
    #include <sphelper.h>
    #include <spuihelp.h>
    #include <comdef.h>
    #define UNICODE
    
    
    
    #include <windows.h>
    #include <windowsx.h>
    #include <commctrl.h>
    
    
    
    #define  WM_RECOEVENT WM_USER+100
    #define GID_SRCMD_CN 1234
    #define MYGRAMMARID  101
    
    
    
    class SREngine
    {
    public:
        SREngine();
        ~SREngine();
    
    
    
    public:
        //speech varibale
        CComPtr <ISpRecognizer> m_cpRecognizer;
        CComPtr <ISpRecoContext> m_cpRecoContext;
        CComPtr <ISpRecoGrammar> m_cpCmdGrammar;
    
    
    
        //audio variable
        CComPtr <ISpAudio> m_cpAudio;
    
    
    
    public:
        HRESULT SetRuleState(const WCHAR * pszRuleName, const WCHAR *pszValue, BOOL fActivate);
        HRESULT LoadCmdFromFile(QString XMLFileName);
        HRESULT InitializeSapi(WId hWnd, UINT Msg);
    
    
    
    };
    
    
    
    #endif // SRENGINE_H
    
    
    
    /*****************************************************************
       SREngine.cpp
    *****************************************************************/
    #include "SREngine.h"
    
    
    
    SREngine::SREngine()
    {
    }
    
    
    
    SREngine::~SREngine()
    {
    }
    
    
    
    HRESULT SREngine::InitializeSapi(WId hWnd, UINT Msg)
    {
        HRESULT hr = S_OK;
        //FOR ONE NOT FOR ALL
        /* 独享模式的配置 */
        hr = m_cpRecognizer.CoCreateInstance( CLSID_SpInprocRecognizer);  //独享模式
        if(FAILED(hr))
        {
            QMessageBox::information(NULL, "Error", "Create recognizer error", MB_OK);
            return hr;
        }
    
    
    
        hr = SpCreateDefaultObjectFromCategoryId(SPCAT_AUDIOIN, &m_cpAudio); //建立默认的音频输入对象
        if(FAILED(hr))
        {
            QMessageBox::information(NULL, "Error", "Create default audio object error", MB_OK);
            return hr;
        }
    
    
    
        hr = m_cpRecognizer ->SetInput(m_cpAudio, TRUE);  //设置识别引擎输入源
        if(FAILED(hr))
        {
            QMessageBox::information(NULL, "Error", "Error setINPUT", MB_OK);
            return hr;
        }
    
    
    
        hr = m_cpRecognizer->CreateRecoContext(&m_cpRecoContext);   //创建识别上下文接口
        if(FAILED(hr))
        {
            QMessageBox::information(NULL, "Error", "Error CreateRecoContext", MB_OK);
            return hr;
        }
        hr =  m_cpRecoContext->SetNotifyWindowMessage(hWnd, Msg, 0, 0);  //设置识别消息,即将Msg消息绑定到hWnd这个窗体上,如果识别出了结果就会产生Msg这个消息,并会emit到hWnd这个窗体
        if(FAILED(hr))
        {
            QMessageBox::information(NULL, "Error", "Error SetNotifyWindowMessage", MB_OK);
            return hr;
        }
        const ULONGLONG ullInterest = SPFEI(SPEI_SOUND_START) | SPFEI(SPEI_SOUND_END) |
                                          SPFEI(SPEI_PHRASE_START) | SPFEI(SPEI_RECOGNITION) |
                                          SPFEI(SPEI_FALSE_RECOGNITION) | SPFEI(SPEI_HYPOTHESIS) |
                                          SPFEI(SPEI_INTERFERENCE) | SPFEI(SPEI_RECO_OTHER_CONTEXT) |
                                          SPFEI(SPEI_REQUEST_UI) | SPFEI(SPEI_RECO_STATE_CHANGE) |
                                          SPFEI(SPEI_PROPERTY_NUM_CHANGE) | SPFEI(SPEI_PROPERTY_STRING_CHANGE);
        hr = m_cpRecoContext->SetInterest(ullInterest, ullInterest);   //设置感兴趣的事件
        if(FAILED(hr))
        {
            QMessageBox::information(NULL, "Error", "Error set interest", MB_OK);
        }
        return hr;
    }
    
    
    
    HRESULT SREngine::LoadCmdFromFile(QString XMLFileName)
    {
        HRESULT hr = S_OK;
    
    
    
        if(!m_cpCmdGrammar)
        {
            hr = m_cpRecoContext ->CreateGrammar(MYGRAMMARID, &m_cpCmdGrammar);  //命令式(command and control---C&C)
            if(FAILED(hr))
            {
                QMessageBox::information(NULL, "Error", "Error Creategammar", MB_OK);
                return hr;
            }
    
    
    
            WCHAR wszXMLFile[256] = L"";
            XMLFileName.toWCharArray(wszXMLFile);  //ASNI TO UNICODE
            //LAOD RULE FROME XML FILE
            hr = m_cpCmdGrammar->LoadCmdFromFile(wszXMLFile, SPLO_DYNAMIC);
            if(FAILED(hr))
            {
                QMessageBox::information(NULL, "Error", "Error LoadCmdFromFile", MB_OK);
                return hr;
            }
        }
        return hr;
    }
    
    
    
    HRESULT SREngine::SetRuleState(const WCHAR * pszRuleName, const WCHAR *pszValue, BOOL fActivate)
    {
        HRESULT hr = S_OK;
        if(fActivate)
        {
            hr = m_cpCmdGrammar ->SetRuleState(pszRuleName, NULL, SPRS_ACTIVE);
        }
        else
        {
            hr = m_cpCmdGrammar ->SetRuleState(pszRuleName, NULL, SPRS_INACTIVE);
        }
        return hr;
    }


    在MainWindow的ui上添加一个“开启”按钮,然后给它添加槽函数: 

    void MainWindow:: on_pushButtonStart_clicked()
    {
        HRESULT hr = m_SREngine.InitializeSapi(this->winId(), WM_RECOEVENT);  //初始化SAPI
        if(FAILED(hr))
        {
            return;
        }
        QString grammarFileName = "../SpeechGrammar.xml";
        hr = m_SREngine.LoadCmdFromFile(grammarFileName);   //创建语法规则
        if(FAILED(hr))
        {
            return;
        }
    
    
    
        /* 激活语音控制 */
        hr = m_SREngine.SetRuleState(NULL, NULL, SPRS_ACTIVE);
        if(FAILED(hr))
        {
            QMessageBox::information(NULL, "Error", "SetRuleState Active Error!", MB_OK);
            return;
        }
        setWindowTitle("Sound Start");
        ui->pushButtonStart->setEnabled(false);
    }


    实现的时候,一个比较棘手的问题是,如何将MFC的消息机制用QT来取代。我首先想到的当然是用信号槽机制来实现,但是这里完全跟信号槽对不上号啊,套不进去啊!!!尝试了半天,在google和baidu上搜啊搜,找到了很少比较相关的资料。没办法我就加QT技术的QQ群,问群里大牛,但可能我没描述清楚,还是解决不了。 
    最后,还是google搜,通过搜QT和WinAPI混合编程,找到了一两个比较好的结果,里面有提到用winEvent来截获窗体的消息,于是看到希望了,哈哈哈。我的winEvent函数如下: 

    bool MainWindow::winEvent(MSG* pMsg, long* result)
    {
        setWindowTitle("Control - Debug: winEvent");
        if(pMsg->message == WM_RECOEVENT)
        {
            *result = this->OnRecoEvent();   //OnRecoEvent函数是具体的处理过程,其中可获取识别结果并对不同的结果做相应的处理
        }
        return false;
    }


    这里的winEvent函数就相当于WinAPI里的WinProc函数,但不需要用信号槽来显式的将信号和槽connect起来,只要有该窗体有消息产生,它就会执行。这样就将消息机制实现了,消息从SAPI的函数里产生,发送到了MainWindow窗体,再在MainWindow的winEvent函数里截获该消息进行相应的处理。 
    该实例的所有源文件已上传到GitHub:https://github.com/ibillxia/Demo/tree/master/QtSAPIDemo

  • 相关阅读:
    汉字数组排序及如何检测汉字
    响应式web布局中iframe的自适应
    CSS3的flex布局
    关于BFC不会被浮动元素遮盖的一些解释
    趣谈unicode,ansi,utf-8,unicode big endian这些编码有什么区别(转载)
    深入seajs源码系列三
    深入seajs源码系列二
    深入seajs源码系列一
    韩国"被申遗" (转自果壳)
    Understanding delete
  • 原文地址:https://www.cnblogs.com/ldjhust/p/3214187.html
Copyright © 2011-2022 走看看