• 安装SpeechRecognition模块 使用recognize_sphinx(),安装PocketSphinx 使用测试: import speech_recognition as sr # obtain audio from the microphone r = sr.Recognizer() print(r) ...pri...

    安装SpeechRecognition模块
    使用recognize_sphinx(),安装PocketSphinx
    在这里插入图片描述使用测试:

    import speech_recognition as sr
    
    # obtain audio from the microphone
    r = sr.Recognizer()
    print(r)
    harvard = sr.AudioFile('input.wav')
    print(harvard)
    with harvard as source:
        audio = r.record(source)
    # recognize speech using Sphinx
    try:
        print("Sphinx thinks you said " + r.recognize_sphinx(audio))
    except sr.UnknownValueError:
        print("Sphinx could not understand audio")
    except sr.RequestError as e:
        print("Sphinx error; {0}".format(e))
    
    

    参考文献:
    https://blog.csdn.net/zhangbijun1230/article/details/83420031

    展开全文
  • 百度离线语音识别

    2019-01-11 15:56:24
    百度离线语音识别简介百度语音离线识别集成离线识别实现合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右...

    简介

    本文记录百度离线识别与讯飞离线语音识别,针对的是应用本身级别的。

    百度语音离线识别集成

    1. 首先下载开发架包:bdasr_V3_20180801_d6f298a.jar,这个是离在线融合的SDK ;

    2. 导入so库:可在下载的demo中找到,复制到自己工程中同名的路径下
      在这里插入图片描述

    3. 需要百度开放平台上去申请 API Key 和 Secret Key,这是认证权限的必备信息,只有认证通过才可以继续使用语音功能,如语音唤醒,在线识别,离线识别等等。首先需要百度开放平台上去申请 API Key 和 Secret Key,这是认证权限的必备信息,只有认证通过才可以继续使用语音功能,如语音唤醒,在线识别,离线识别等等。
      在这里插入图片描述
      而鉴权认证就是要成功获取到AccessToken(Access Token 是用户身份验证和授权的凭证),而这个认证过程需要自己在应用中编写,之后会提到。
      将获取到的AppID、API Key 和 Secret Key添加到项目的配置文件中去;

    4. AndroidManifest.xml 文件的配置:
      设置权限(如果是Android 6.0 以上还需要获取动态权限,之后会说到)

        <uses-permission android:name="android.permission.RECORD_AUDIO" />
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
        <uses-permission android:name="android.permission.INTERNET" />
        <uses-permission android:name="android.permission.READ_PHONE_STATE" />
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    

    设置APP_ID, APP_KEY, APP_SECRET

        <meta-data android:name="com.baidu.speech.APP_ID"
            android:value="9788136" />
        <meta-data
            android:name="com.baidu.speech.API_KEY"
            android:value="0GjQNO5H4pGPf9HyA3AmZEbz" />
        <meta-data
            android:name="com.baidu.speech.SECRET_KEY"
            android:value="db981ef3ec647ba8a09b599ad7447a24" />
    
    1. 注意事项:在申请API Key时需要设置应用包名,并且保证官网申请时的与app/bulid.gradle里的applicationId一致,否则会导致离线命令词或是唤醒报“no licenece”的错误。

    离线识别实现

    1.可以先准备本地词库和唤醒词库

    2.基于SDK的使用:

    • 初始化EventManager类
     EventManager  asr = EventManagerFactory.create(context, "asr");//语音识别器
     EventManager  wp = EventManagerFactory.create(context, "wp");//语音唤醒器
    
    • 定义输出事件类
    public class RecogResultManager implements EventListener{
        ...........
        
         /**
         * 回调事件
         */
        @Override
        public void onEvent(String name, String params, byte[] data, int offset, int length) {
            String logMessage = "name:" + name + "; params:" + params;
            Log.d(TAG, logMessage);
            switch (name) {
                case SpeechConstant.CALLBACK_EVENT_ASR_READY:
                    // 引擎准备就绪,可以开始说话              
                    break;
                case SpeechConstant.CALLBACK_EVENT_ASR_BEGIN:
                    // 检测到用户的已经开始说话
                    break;
                case SpeechConstant.CALLBACK_EVENT_ASR_END:
                    // 检测到用户的已经停止说话
                    break;
                case SpeechConstant.CALLBACK_EVENT_ASR_PARTIAL:
                    // 临时识别结果, 长语音模式需要从此消息中取出结果
                    break;
                  case SpeechConstant.CALLBACK_EVENT_ASR_FINISH:
                    // 识别结束, 最终识别结果或可能的错误
                    break;
                case SpeechConstant.CALLBACK_EVENT_ASR_LONG_SPEECH:
                    // 长语音
                    break;
                case SpeechConstant.CALLBACK_EVENT_ASR_VOLUME:
                    //音量值 
                    break;
                case SpeechConstant.CALLBACK_EVENT_ASR_AUDIO:
                    if (data.length != length) {
                     //可能出错的地方:回调返回的数据有问题
                       Log.d(TAG, "internal error: asr.audio" +
                                " callback data length is not equal to length param");
                     .....
                    }
                    break;
                case SpeechConstant.CALLBACK_EVENT_WAKEUP_SUCCESS:
                    //语音唤醒成功 
                    break;
                case SpeechConstant.CALLBACK_EVENT_WAKEUP_ERROR:
                    //语音唤醒失败
                    break;
                .......
    
                default:
                    break;
            }
        }
    }    
    
    
    
    • 注册自己的输出事件类
    asr.registerListener(eventListener);
    wp.registerListener(eventListener);
    
    • 加载唤醒词库与离线词库
     /**
     - 加载唤醒词库
      */
        private void loadWakeup() {
            Map<String, Object> params = new HashMap<>();
            params.put(SpeechConstant.WP_WORDS_FILE, "assets://WakeUp.bin");
            //开始识别语音唤醒
            mWakeup.start(params);
        }
        
     /**
     - 加载离线词库
      */
        private void loadWakeup() {
           Map<String, Object> params = new LinkedHashMap<>();
           //设置此参数使用与语音唤醒后进入语音识别模式
            params.put(SpeechConstant.VAD, SpeechConstant.VAD_DNN);
            params.put(SpeechConstant.DECODER, 2);
            params.put(SpeechConstant.ASR_OFFLINE_ENGINE_GRAMMER_FILE_PATH, "assets://baidu_speech_grammar.bsg");
           //设置唤醒到识别的停顿时间
            if (backTrackInMs > 0) {
                params.put(SpeechConstant.AUDIO_MILLS, System.currentTimeMillis() - backTrackInMs);
            }
            mRecognizer.cancel();
            //开始进行识别
            mRecognizer.start(params);
        }
         注:对于语音唤醒到识别有两种方案:
         方案1: backTrackInMs > 0,唤醒词说完后,直接接句子,中间没有停顿,
                开启回溯,连同唤醒词一起整句识别,推荐4个字 1500ms,backTrackInMs 最大 15000,即15s.
         方案2: backTrackInMs = 0,唤醒词说完后,中间有停顿,不开启回溯。唤醒词识别回调后,正常开启识别。
                官方demo里采用默认设置的是1500,本人demo中选择的是方案2,因为测试结果方案2的识别效果好一些,详见下面测试结果。
    
    • 查询权限,获取AccessToken
    public void check() {
            appendLogMessage("try to check appId " + appId + " ,appKey=" + appKey + " ,secretKey" + secretKey);
            if (appId == null || appId.isEmpty()) {
                  errorMessage = "appId 为空";
                  fixMessage = "填写appID";
              }
            if (appKey == null || appKey.isEmpty()) {
                    errorMessage = "appKey 为空";
                    fixMessage = "填写appID";
              }
            if (secretKey == null || secretKey.isEmpty()) {
                     errorMessage = "secretKey 为空";
                      fixMessage = "secretKey";
             }
            try {
                    checkOnline();
            } catch (UnknownHostException e) {
                    infoMessage = "无网络或者网络不连通,忽略检测 : " + e.getMessage();
            } catch (Exception e) {
                    errorMessage = e.getClass().getCanonicalName() + ":" + e.getMessage();
                    fixMessage = " 重新检测appId, appKey, appSecret是否正确";
             }
     }
    
    /**
     *  获取并校验token
     */
      public void checkOnline() throws Exception {
                String urlpath = "http://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials&client_id="
                        + appKey + "&client_secret=" + secretKey;
                URL url = new URL(urlpath);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                conn.setConnectTimeout(1000);
                InputStream is = conn.getInputStream();
                BufferedReader reader = new BufferedReader(new InputStreamReader(is));
                StringBuilder result = new StringBuilder();
                String line = "";
                do {
                    line = reader.readLine();
                    if (line != null) {
                        result.append(line);
                    }
                } while (line != null);
               String res = result.toString();
               if (!res.contains("audio_voice_assistant_get")) {
                    errorMessage = "appid:" + appId + ",没有audio_voice_assistant_get 权限,请在网页上开通\"语音识别\"能力";
                    fixMessage = "secretKey";
                    return;
                }
                appendLogMessage("openapi return " + res);
                JSONObject jsonObject = new JSONObject(res);
                String error = jsonObject.optString("error");
                if (error != null && !error.isEmpty()) {
                    errorMessage = "appkey secretKey 错误" + ", error:" + error + ", json is" + result;
                    fixMessage = " 重新检测appId对应的 appKey, appSecret是否正确";
                    return;
                }
               String token = jsonObject.getString("access_token");
               if (token == null || !token.endsWith("-" + appId)) {
                    errorMessage = "appId 与 appkey及 appSecret 不一致。
                    appId = " + appId + " ,token = " + token;
                    fixMessage = " 重新检测appId对应的 appKey, appSecret是否正确";
                }
            }
        }
    
    • 加载动态权限(Android 6.0以上需要)
      在主Activity中添加:
    
     private void initPermission() {
            String[] permissions = {Manifest.permission.RECORD_AUDIO,
                    Manifest.permission.ACCESS_NETWORK_STATE,
                    Manifest.permission.INTERNET,
                    Manifest.permission.READ_PHONE_STATE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE
            };
            ArrayList<String> toApplyList = new ArrayList<>();
           for (String perm : permissions) {
                if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this, perm)) {
                    toApplyList.add(perm);
                    // 进入到这里代表没有权限.
                }
            }
            String[] tmpList = new String[toApplyList.size()];
            if (!toApplyList.isEmpty()) {
                ActivityCompat.requestPermissions(this, toApplyList.toArray(tmpList), 123);
            }
        }
        
    @Override
       public void onRequestPermissionsResult(int requestCode,
                                               @NonNull String[] permissions,
                                               @NonNull int[] grantResults) {
            // 此处为android 6.0以上动态授权的回调,用户自行实现。
        }
    
    • 控制识别/唤醒
       /**
        * 语音唤醒开始
        */
        public void start(Map<String, Object> params) {
            String json = new JSONObject(params).toString();
            wp.send(SpeechConstant.WAKEUP_START, json, null, 0, 0);
        }
        
        /**
         * 停止语音唤醒
         */
        public void stop() {
            wp.send(SpeechConstant.WAKEUP_STOP, null, null, 0, 0);
        }
    
        /**
         * 开始识别
         */
        public void start(Map<String, Object> params) {
            String json = new JSONObject(params).toString();
            asr.send(SpeechConstant.ASR_START, json, null, 0, 0);
        }
        
        /**
         * 提前结束录音等待识别结果。
         */
        public void stop() {
            if (!isInited) {
                throw new RuntimeException("release() was called");
            }
            asr.send(SpeechConstant.ASR_CANCEL, "{}", null, 0, 0);
        }
        
        /**
         * 取消本次识别,取消后将立即停止不会返回识别结果。
         * cancel 与stop的区别是 cancel在stop的基础上,完全停止整个识别流程,
         */
        public void cancel() {
            if (!isInited) {
                throw new RuntimeException("release() was called");
            }
            asr.send(SpeechConstant.ASR_CANCEL, "{}", null, 0, 0);
        }
    
    • 事件管理器退出,回收资源,不能一直占用着mic资源。
        /**
         * 释放语音唤醒资源
         */
        public void release() {
           if (wp== null) {
                return;
            }
            stop();
            wp.unregisterListener(eventListener);
            wp = null;
            isInited = false;
        }
       
        /**
         * 释放语音识别资源
         */
          public void release() {
            if (asr == null) {
                return;
            }
            cancel();
            asr.send(SpeechConstant.ASR_KWS_UNLOAD_ENGINE, null, null, 0, 0);
            asr.unregisterListener(eventListener);
            asr = null;
            isInited = false;
        }
    

    添加动作识别:

    之前考虑是用数据库匹配识别结果,来找到相应的组件,但是demo里做得着急,只是通过管理activity,以activity和name为媒介来找到相应的组件,从而执行相应的动作。

    public abstract class BaseActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            VrApplication.getInstance().addActivity(this);
        }
        @Override
        protected void onDestroy() {
            super.onDestroy();
            VrApplication.getInstance().removeActivity(this);
        }
    }
    
    public class VrApplication extends Application {
    
        private static final VrApplication INSTANCE = new VrApplication();
    
        private  List<BaseActivity> mActivities;
    
        private VrApplication() {}
    
        public static VrApplication getInstance() {
            return VrApplication.INSTANCE;
        }
    
        public  void addActivity(BaseActivity activity){
            if(mActivities == null){
                mActivities = new ArrayList<>();
            }
            mActivities.add(activity);
        }
        
          public  void removeActivity(BaseActivity activity){
            mActivities.remove(activity);
        }
    
        public List<BaseActivity> getActivities() {
            return mActivities;
        }
    
        /**
         * 关闭应用后需要清空管理
         */
         public  void finishAll(){
            for(BaseActivity activity : mActivities){
                if(! activity.isFinishing()){
                    activity.finish();
                }
            }
        }
    }
    

    所有的界面activity需要继承BaseActivity,初始化组件时保存起来,界面销毁时clear()。

     private void initView() {
            mViews = new HashMap<>();
            EditText etCode = (EditText) findViewById(R.id.tv_point_code);
            Button action = (Button) findViewById(R.id.btn_action);
            mViews.put(etCode.getHint().toString(), etCode);
            mViews.put(action.getText().toString(), action);
        }
    

    回调监听处理结果,进行确认组件去执行相应的动作:

    public class RecogResultManager implements EventListener, IStatus {
         ...............
         
        /**
         * 回调事件
         */
        @Override
        public void onEvent(String name, String params, byte[] data, int offset, int length) {
               switch (name) {
                    ........
                    case SpeechConstant.CALLBACK_EVENT_ASR_PARTIAL:
                         // 临时识别结果, 长语音模式需要从此消息中取出结果
                         handlePartial(params, data, offset, length);
                         break;
                    case SpeechConstant.CALLBACK_EVENT_ASR_FINISH:
                         // 识别结束, 最终识别结果或可能的错误
                         handleFinish(params);
                         break;
                    ........
                    case SpeechConstant.CALLBACK_EVENT_WAKEUP_SUCCESS:
                         //语音唤醒成功
                         handleWpSuccess(name, params);
                         break;
                    case SpeechConstant.CALLBACK_EVENT_WAKEUP_ERROR:
                         handleWpErro(name, params);
                         break;
              }
        } 
        ........
    }               
    

    处理语音唤醒,语音唤醒后,要切换开始进行语音识别:

    private void handleWpMsg() {
            Map<String, Object> mParams = new LinkedHashMap<>();
            mParams.put(SpeechConstant.VAD, SpeechConstant.VAD_DNN);
            mParams.put(SpeechConstant.DECODER, 2);
            mParams.put(SpeechConstant.ASR_OFFLINE_ENGINE_GRAMMER_FILE_PATH, BSGPATH);
             if (mBackTrackInMs > 0) {
                mParams.put(SpeechConstant.AUDIO_MILLS, System.currentTimeMillis() - mBackTrackInMs);
            }
            mRecognizer.cancel();
            mRecognizer.start(mParams);
     }
    

    处理语音识别结果:

    private void analysData(RecogResult recogResult) {
            String results = recogResult.getBestResult();
            //从缓存中获取当前的Activity
            BaseActivity activityInstance = UiUtil.getActivityInstance(mContext);
            if (activityInstance == null) {
                return;
            }
            Map<String, View> views = activityInstance.getViews();
            for (Map.Entry<String, View> entry : views.entrySet()) {
                if (results.contains(entry.getKey())) {
                    action(entry.getValue(), results);
                }
            }
      }
    

    执行动作:

     private void action(View value, String results) {
            if (value instanceof Button) {
                value.performClick();
            } else if (value instanceof EditText) {
                ((EditText) value).setText(results);
            }
     }
    

    注意事项

    1.即使只进行离线操作,第一次也需要进行联网进行认证授权才可以进行接下来的语音识别。
    2. 不用时要及时释放,不能一直占用mic资源,影响其他使用。
    3. 环境嘈杂时,识别率会很低,需要依赖降噪算法。
    4.本demo针对一个应用来说,通过语音识别命令,并执行操作,跳转界面,点击操作,选择操作等,有一定的局限性,因为离线时,只能识别词库中的词,所以EditText或是TextView填写值的时候,可能没有用户所说的那个值,目前只是依赖在线识别来控制这块,还有待进一步研究。

    demo下载地址:

    链接: https://download.csdn.net/download/weixin_44328479/10910474.
    Alt

    展开全文
  • 从科大讯飞官方Demo中提取的语音识别部分代码加以修改,可实现在线与离线语音识别,下载后需要填写自己的APPID以及修改成与ID相匹配的讯飞SDK,实测可运行。
  • **关于离线语音识别的相关问题 最近在看关于AI音箱相关的资料,想请教大牛们, 关于离线语音识别,好多文章说的是使用神经网络技术进行特征提取和建模等来识别指令,那离线识别和云识别不知道是不是都是这一个模型,...

    **关于离线语音识别的相关问题

    最近在看关于AI音箱相关的资料,想请教大牛们,
    关于离线语音识别,好多文章说的是使用神经网络技术进行特征提取和建模等来识别指令,那离线识别和云识别不知道是不是都是这一个模型,离线识别是不是要简单写,找了一款离线识别的芯片:WEGASUN-M6 ,好像是把离线词条直接编辑到文本中就可以了,然后利用TTS技术转换成语音的,这个好像不需要经过神经网络的建模和学习,实际上是这样子的吗?
    有咩有大牛比较懂得,望指教

    展开全文
  • 本项目是做出的产品是一个很大的一个Windows下的增强现实系统,产品功能已经基本完善,考虑到给用户带来更好的体验,故综合评估后采用讯飞语音识别方案进行嫁接。 项目介绍:1)打开系统时启动语音识别,检测到用户...

    本项目做出的产品是一个Windows下的增强现实系统,系统很庞大,产品功能已经基本完善,考虑到给用户带来更好的体验,故综合评估后采用讯飞语音识别方案进行嫁接。

    项目介绍:

    1)打开系统时启动语音识别,检测到用户说出关键词(如:上一步,下一步,结束等)时,系统自动进行相应的操作;

    2)不需要按任何按键进行连续的语音识别控制,延迟为2秒左右;

    3)可以识别词语,句子,中文,英文数字字母通吃,不限语速。并且语句结束判断机制很智能;

    3)离线!离线!现在做一个基于windows的离线的语音识别系统在网络上还真没谁,讯飞自己也还没有推出这款SDK,我是在讯飞的离线命令词识别SDK开发包上开发出的介于两者之间功能的系统。

    为什么用讯飞:

    之前使用过语音识别开发版,用驻极体采集声音,通过串口传输信号来进行识别,只能讲开发版只适合用来做个课程设计啥的玩玩,做产品还是太LOW,识别率地下并且识别范围窄,貌似只能识别数字字母;之后调查过汉语语音识别库如Sphinx,Julius,百度语音等。去网上问,很多人推荐这些方案的,我没试过,不过一个SDK也只有自己正真开发过才知道好不好用强不强大,说的再牛逼也是在别人手上玩的转,到你这就不一定了。我用讯飞就是看上它的鲁棒,免费,毕竟是久经考验的平台,讯飞有一个开发者论坛,开发别人的SDK有个社区很重要,不然有问题没地方问。


    项目开发经验与源码如下:

    一、跑通讯飞离线命令词识别SDK

    首先去讯飞官网下载相应SDK,要注册登录,绑定产品才能下载,下载后跑一跑给的demo,跑不通的话去讯飞的论坛找答案。

    我用的demo是asr_record_demo,这个demo可以实现的功能是按下R键从mic中录制缓冲音频并按下S键后进行识别。

    稍微看一看这个demo中的东西吧,把该删的东西删了,该修整的修整,玩转了demo才能进一步做移植。

    二、文件分析与移植

    把SDK包中的需要的.lib  .h  .c和所需要的其余文件拷贝到项目相应的文件夹去,最好是分个类,看起来比较清楚。它比较重要的几个文件有以下几个:

    msc.lib   讯飞语音离线命令词识别所依赖的库(X64版本为msc_x64.lib)才15KB就能有如此强大的功能。

    call.bnf    采用巴科斯范式语法规则的自定义语法库(call是根据自己需要命名的),用来编写语音识别过程中的语法规则,这个语法写的好语音识别更智能。

    speech_recognizer.c  基于录音接口和MSC接口封装一个MIC录音识别的模块,SDK的主要文件

    winrec.c  封装windows录音,Windows平台下SDK的主要文件


    好了,既然demo已经知道怎么用了,把demo所依赖的所有文件和库照搬进自己的项目去,就可以在自己的项目中调用讯飞的函数了(注意头文件和库文件的路径问题)。

    三、使用讲解

    先看看我的语法文件

    call.bnf:


    巴科斯范式语法语法文件怎么写在讯飞的论坛里有很多资源自己去看吧,我写的比较简单,写得好的语法可以更加智能。


    大致讲讲源码:

    为了看起来简洁,我在speech_recognizer.h中做了一个类SR,把主文件中的一些函数定义和一些宏搬进去。

    speech_recognizer.h:

    class SR {
    public:
    
    	const char * ASR_RES_PATH = "fo|res/asr/common.jet";  //离线语法识别资源路径
    #ifdef _WIN64
    	const char * GRM_BUILD_PATH = "res/asr/GrmBuilld_x64";  //构建离线语法识别网络生成数据保存路径
    #else
    	const char * GRM_BUILD_PATH = "res/asr/GrmBuilld";  //构建离线语法识别网络生成数据保存路径
    #endif
    	const char * GRM_FILE = "call.bnf"; //构建离线识别语法网络所用的语法文件
    
    	int build_grammar(UserData *udata);//构建语法
    	int run_asr(UserData *udata);//启动引擎
    
    };
    speech_recognizer.c:

    #pragma comment(lib,"../../lib/msc_x64.lib") //x64
    #else
    #pragma comment(lib,"XXXXXX/lib/msc.lib") //x86
    #endif
    这里的库目录要注意,用相对路径可能找不到文件,可以使用绝对路径

    #ifdef _WIN64
    <pre name="code" class="cpp">static int update_format_from_sessionparam(const char * session_para, WAVEFORMATEX *wavefmt)
    {
    	char *s;
    	/*if ((s = strstr(session_para, "sample_rate"))) {
    		if (s = strstr(s, "=")) {
    			s = skip_space(s);
    			if (s && *s) {
    				wavefmt->nSamplesPerSec = atoi(s);
    				wavefmt->nAvgBytesPerSec = wavefmt->nBlockAlign * wavefmt->nSamplesPerSec;
    			}
    		}
    		else
    			return -1;
    	}
    	else {
    		return -1;
    	}*/
    
    	return 0;
    }

    
    如果加到讯飞提供的demo使用的是VS2010如果放到VS2015中就会出一些语法上的错,自己看着改一改,比如上面这个函数,是更新用户词典用的,我不需要但是又存在语法错误,就把它的内容直接注释掉得了。
    

    我的项目的主文件中:

    首先定义几个宏:

    UserData asr_data;//语音识别用户配置
    speech_rec asr;//麦克风输入存储结构体
    SR sr;//语音识别实体
    string SPEAKER="";//用于缓存语音识别内容
    
    在程序初始化处配置如下:

    openVoiceRecognizer = true;//语音识别开关
    	if (openVoiceRecognizer)
    	{
    		const char *login_config = "appid = XXXXX"; //登录参数
    		int ret = 0;
    
    
    		ret = MSPLogin(NULL, NULL, login_config); //第一个参数为用户名,第二个参数为密码,传NULL即可,第三个参数是登录参数
    		if (MSP_SUCCESS != ret) {
    			printf("登录失败:%d\n", ret);
    			openVoiceRecognizer = false;
    		}
    
    
    		memset(&asr_data, 0, sizeof(UserData));
    		printf("构建离线识别语法网络...\n");
    		ret = sr.build_grammar(&asr_data);  //第一次使用某语法进行识别,需要先构建语法网络,获取语法ID,之后使用此语法进行识别,无需再次构建
    		if (MSP_SUCCESS != ret) {
    			printf("构建语法调用失败!\n");
    			openVoiceRecognizer = false;
    		}
    
    
    		while (1 != asr_data.build_fini)
    			_sleep(300);
    		if (MSP_SUCCESS != asr_data.errcode)
    			_sleep(300);
    		printf("离线识别语法网络构建完成,开始识别...\n");
    		ret = sr.run_asr(&asr_data);//预启动语音识别引擎
    	}
    
    在初始化这里我修改了 sr.run_asr(&asr_data);这个函数,我把它调用的sr_start_listening(&asr);函数抽离出来,不让它启动识别引擎而只让它预启动,把所有结构体,变量先初始化待命。

    void recognize_mic(const char* session_begin_params)  //根据自己项目需要写的语音识别预热函数
    {
    	int errcode;
    	HANDLE helper_thread = NULL;
    
    	struct speech_rec_notifier recnotifier = {
    		on_result,
    		on_speech_begin,
    		on_speech_end
    	};
    	errcode = sr_init(&asr, session_begin_params, SR_MIC, DEFAULT_INPUT_DEVID, &recnotifier);
    	if (errcode) {
    		printf("speech recognizer init failed\n");
    		return;
    	}
    
    	/*	while (1) {
    			errcode = sr_start_listening(&asr);//我把它调用的sr_start_listening(&asr);函数抽离出来,不让它启动识别引擎而只让它预启动,把所有结构体,变量先初始化待命。
    		}
    
    	exit:
    		sr_uninit(&asr);*/
    }
    
    int SR::run_asr(UserData *udata)
    {
    	char asr_params[MAX_PARAMS_LEN] = { NULL };
    	const char *rec_rslt = NULL;
    	const char *session_id = NULL;
    	const char *asr_audiof = NULL;
    	FILE *f_pcm = NULL;
    	char *pcm_data = NULL;
    	long pcm_count = 0;
    	long pcm_size = 0;
    	int last_audio = 0;
    	int aud_stat = MSP_AUDIO_SAMPLE_CONTINUE;
    	int ep_status = MSP_EP_LOOKING_FOR_SPEECH;
    	int rec_status = MSP_REC_STATUS_INCOMPLETE;
    	int rss_status = MSP_REC_STATUS_INCOMPLETE;
    	int errcode = -1;
    	int aud_src = 0;
    
    	//离线语法识别参数设置
    	_snprintf(asr_params, MAX_PARAMS_LEN - 1,         //<span style="font-family: Arial, Helvetica, sans-serif;">离线语法识别参数根据自己的需要进行更改</span>
    
    		"engine_type = local, \
    		asr_res_path = %s, sample_rate = %d, \
    		grm_build_path = %s, local_grammar = %s, ",
    		sr.ASR_RES_PATH,
    		SAMPLE_RATE_16K,
    		sr.GRM_BUILD_PATH,
    		udata->grammar_id
    		);
    
    	recognize_mic(asr_params);
    	return 0;
    }
    
    而后是三个语音识别的中间过程和结果处理的函数:

    void on_result(const char *result, char is_last)   //根据自己的需要写结果处理
    {
    	char *p = "上一步";
    	char *pq = "下一步";
    	char *q = "扫频仪操作演示";
    	char *end1 = "结束";
    	char *end2 = "退出";
    	if (strstr(result, p)) {
    		SPEAKER = "上一步";	
    	}
    	else if (strstr(result, pq)) {
    		SPEAKER = "下一步";
    	}
    	else if (strstr(result, q)) {
    		SPEAKER = "扫频仪操作演示";
    	}
    	else if (strstr(result,end1)|| strstr(result, end2)) {
    		SPEAKER = "退出";
    	}
    	cout << SPEAKER << endl;
    }
    void on_speech_begin()
    {
    	if (g_result)
    	{
    		free(g_result);
    	}
    	g_result = (char*)malloc(BUFFER_SIZE);
    	g_buffersize = BUFFER_SIZE;
    	memset(g_result, 0, g_buffersize);
    
    	printf("Start Listening...\n");
    }
    void on_speech_end(int reason)
    {
    	if (reason == END_REASON_VAD_DETECT)
    		printf("\nSpeaking done \n");
    	else
    		printf("\nRecognizer error %d\n", reason);
    }
    
    最后就是在自己项目哪里需要语音识别就在哪里抛出缓冲线程启动识别引擎:

    if (openVoiceRecognizer) {
    		
    		sr_start_listening(&asr);//抛出缓冲线程进行语音识别
    
    		if (SPEAKER=="下一步") {
    			keyPressed(' ');
    		}
    		else if (SPEAKER == "上一步") {
    			keyPressed('b');
    		}
    		else if (SPEAKER == "扫频仪操作演示") {
    			keyPressed('1');
    		}
    		else if (SPEAKER=="退出") {
    			std::exit(0);
    		}
    		SPEAKER = "";
    	}

    上面代码中的关键就是sr_start_listening(&asr);这个函数,前面也说了这是从run_asr()调用的方法中抽离出来的,抽离出来后run_asr()就变成了预热函数,只需在程序初始化的时候调用它后面的语音识别就不要重复调用了,节省资源。

    必须要说说这个关键函数sr_start_listening(&asr);

    这是讯飞这款SDK中不需要动的最后一个封装好的函数,里面的东西不要动,前面的东西配置好一调用它就可以进行语音识别了,这个函数中的东西是这款SDK的精华,实现过程很复杂不需要管,但是要记住它的几个特性:

    1)一调用它就相当于抛出了一个带缓冲的新线程,这个线程独立进行语音的识别可以不干扰项目的主循环的进行。

    2)这个线程寿命是自动的,程序一开始启动到这里调用这个函数后启动线程,当程序的主循环又回来这里不会重复启动这个线程,而是该线程识别完用户的语音判断语句结束机制触发后才待命,等待下一次循环的启动。这句话有些费解,用我的项目解释下:我的项目在update()函数中调用sr_start_listening(&asr); 我的update()函数每100毫秒循环一次,第一次循环启动这个函数,抛出线程进行语音识别,语音识别用了10秒结束,在这10秒过程中update()循环了100次,但是只启动该函数一次,第101次循环的时候就可以第二次启动该函数了,同时在上面代码片中我用SPEAKER转存的字符串的判断条件已经成立,就可以进行相应的操作了,在这里我触发了不同的按键来代替各种操作。

    3)举个栗子,我在update()中可以在每一次循环时画一帧图片的方式实现一个视频的播放,而在这里面调用语音引擎sr_start_listening(&asr);,在引擎识别的过程中会不会中断视频的播放或者视频出现卡帧或迟滞呢?答案是不会,上面也解释了,这个函数抛出一个独立的线程,不影响主函数的循环。所以可以用这种方法实现用语音控制视频的播放:快进、后退、暂停、截屏、全屏、音量加大、切换到高清......是不是很酷!

    上面的几个源码文件我打了个包,下载看可能更清楚:

    http://download.csdn.net/detail/sac761/9647029

    四、总结

    我用这个方案实现了离线的语音控制系统,语音识别率达到百分之九十以上,而且词语句子中英文通吃,实时性强,系统鲁棒。

    畅想一下用这个方法加方案开发这几个产品:

    语音控制的媒体播放器,功能在上面也讲了,很酷的!

    语音控制的PPT遥控器

    语音控制的AR/VR系统

    语音交互智能眼镜

    .............





    展开全文
  • 4 月 13 日-4 月 14 日,CSDN 将在北京主办“Python 开发者日( 2019 )”,汇聚十余位来自阿里巴巴、IBM、英伟达等国内外一线科技公司的Python 技术专家,还有数百位来自各行业领域的 Python 开发者,共同探讨...

    4 月 13 日-4 月 14 日,CSDN 将在北京主办“Python 开发者日( 2019 )”,汇聚十余位来自阿里巴巴、IBM、英伟达等国内外一线科技公司的Python 技术专家,还有数百位来自各行业领域的 Python 开发者,共同探讨Python在不同场景下的的应用优势和实战经验。目前5折早鸟票限量发售中!

    购票地址:https://bss.csdn.net/m/topic/python_developer

    作者 | 琥珀

    出品 | AI科技大本营(公众号id:rgznai100)

    Python规划学习路线图,速领取?

    https://edu.csdn.net/topic/python115?utm_source=csdn_bw

     

     

    谷歌语音输入法可离线识别啦!

    这次出手的,又是谷歌 AI 团队。刚刚,他们为旗下的一款手机输入法 Gboard (不要跟谷歌拼音输入法搞混了啊~)上线了新功能:离线语音识别。目前这一新功能,只能在其自家的产品 Pixel 系列手机上使用。

    广大已经下载或正在赶往下载路上的 Pixel 圈外人士,包括 iOS 用户可能都会失望了。

    他们是这样描述这款新功能的配置的:端到端、全神经、本地部署的语音识别系统。

    在其最近的论文 “Streaming End-to-End Speech Recognition for Mobile Devices” 中,他们提出了一种基于 RNN-T(RNN transducer)的训练模型。

     

    它非常紧凑,可满足在手机上部署。这意味着不会出现太多网络延迟或紊乱,即使用户处于脱机状态,这款语音识别系统也始终可用。该模型始终以字符级工作, 因此即便你说话,它也会逐个字符地输出单词,就好像有人在实时键入并准确在虚拟键盘听写出你说的话。

    例如,下面两张图片中展示的是在听写系统中输入相同句子时的情况展示:左侧为服务器端,右侧为本地端。哪边的语音识别体验更好呢?

    总结起来就是,“离线状态下,没有任何延迟。”这也是谷歌此次亮出的大杀器。

    发生延迟是因为你的语音数据必须从手机传输到服务器上,解析完成后再返回。这可能需要几毫秒甚至几秒的时间。万一语音数据包在以太网中丢失,则需要更长的时间。

    将语音转换成毫秒级的文本需要相当多的计算力。这不只简单是听到声音然后写一个单词那么简单,而是需要理解一个人讲话的含义,以及背后涉及的很多有关语言和意图的上下文语境。

    在手机上是可以做到这一点的,但如此的话,又会很损耗电池电量。

    语音识别模型简史

    一般来讲,语音识别系统由几个部分组成:将音频片段(通常为 10 毫秒帧)映射到音素的声学模型、将音素连接起来形成单词的发声模型,以及一个表达给定模型的语言模型。在早期系统,这些组件是相对独立优化的。

    2014 年左右,研究人员开始专注于训练单个神经网络,将输入音频波形直接映射到输出句子。通过在给定一系列音频特征的情况下生成一系列单词或字形来学习模型,这种 sequence-to-sequence 的方法促使了 attention-based 和 listen-attend-spell(LAS)模型的诞生。虽然这些模型在准确性方面表现出极大的前景,但它们通常会检查整个输入序列,并且在输入时不允许输出,这是实时语音转录的必要特征。

    同时,一种称为 connectionist temporal classification(CTC)的技术有助于减少当时识别系统的延时问题。这对于后来创建 RNN-T 架构是一次重要的里程碑,也被看作是 CTC 技术的一次泛化。

    (编者注:CTC,其全称为 Connectionist Temporal Classfication,由 Graves 等人于 2006 年提出,用于训练递归神经网络(RNN)以解决时序可变的序列问题。它可用于在线手写识别或识别语音音频中音素等任务。发展到如今,CTC 早已不是新名词,它在工业界的应用十分成熟。例如,在百度近日公布的在线语音识别输入法中,其最新语音模型在 CTC 的基础上还融合了 Attention 等新技术。)

    何为RNN-T?

    RNN-T 是一种不采用注意力机制的 sequence-to-sequence 模型。与大多数 sequence-to-sequence 模型(通常需要处理整个输入序列(在语音识别中即是波形)以产生输出句子)不同,RNN-T 会连续处理输入样本和流输出符号。

    输出符号是字母表的字符。RNN-T 会逐个输出字符,并在适当的位置输入空格。它通过反馈循环执行此操作,该训练将模型预测的符号反馈到其中以预测下一个符号。如下图所示。

    用输入音频样本 x 和预测符号 y 表示 RNN-T。预测符号(Softmax 层的输出)通过预测网络反馈到模型中。

    有效训练这样的模型已经很困难,但随着新开发的训练技术进一步将单词错误率降低了 5%,它的计算强度变得更高。为了解决这个问题,研究人员开发了一个并行实现过程,因此 RNN-T 损失功能可以在 Google Cloud TPU v2 上大批量运行。训练中实现了大约 3 倍的加速。

    离线识别

    在传统的语音识别引擎中,声学、发声和语音模型组合成一个大的图搜索(search graph),其边缘用语音单元及其概率标记。当语音波形呈现给识别系统时,“解码器”在给定输入信号的情况下会搜索图中相似度最高的路径,并读出该路径所采用字序列。通常,解码器采用基础模型的有限状态传感器(Finite State Transducer, FST)表示。然而,尽管有复杂的解码技术,图搜索仍很困难,因为生产模型几乎有 2GB 大小。这可不是在移动电话上想托管就可以实现的,因此这种方法需要在线连接才能正常使用。

    为了提高语音识别的有效性,研究人员尝试直接在设备上托管新模型以避免通信网络的延迟和固有的不可靠性。因此,端到端的方法不需要在大型解码器图上进行搜索。相反,解码器包括通过单个神经网络的集束搜索(beam search)。RNN-T 与传统的基于服务器端的模型具有相同的精度,但前者只有 450MB,而且更加智能地使用参数和打包信息。但即便在如今的智能手机上,450MB 还是占用了很大的空间,例如通过大型网络是信号传播可能会很慢。

    因此,研究人员通过使用参数量化和混合内核技术进一步减小了模型大小。这项技术早在 2016 年就已发布,并在 TensorFlow Lite 版本中提供公开的模型优化工具包。模型量化相对于训练的浮点模型提供 4 倍压缩,在运行时实现了 4 倍加速,这使得 RNN-T 比单核上的实时语音运行得更快。压缩后,最终模型大小只占 80MB。

    效果如何?

    谷歌公开这一新功能后,TechCrunch 评论称,“鉴于 Google 的其他产品几乎没有是离线工作的,那么你会在离线状态下写一封电子邮件吗?当然,在网络条件不好的情况下,这款应用新功能可能会解决了用户痛点,但显然,这还是有点讽刺(鸡肋)。”

    而这也一度吸引来了 HackerNews 上不少用户评论,他们也将部分矛头指向了所谓的“离线功能”:

    “离线功能虽然不是最主要的吸引力,但正如本文中提到的,延迟问题的减少是巨大的。他们可能没有提及的是对隐私问题的影响。不过,用户一般不会离线处理事物,但如果需要来回的稳定数据包流,连接网络也是很麻烦的问题。”

     

    不过,经过尝试后的用户还是非常看好:“我只是将我的 Pixel1 代切换到飞行模型,并尝试了语音输入。果然,它的离线工作速度很快!这令人非常印象深刻(我之前尝试过,但过去它只能理解一些特殊的短语。)

     

    有多好方法可以实现这一功能呢,但我认为任何应用都能从这次语音的改进中受益。“

    为此,营长也特意下载了 Gboard、讯飞、百度三家语音输入法,试看它们在飞行模式下的效果如何。

    Round 1

     

    Gboard:目前非 Pixel 手机中离线语音尚无法使用,且针对某些机型甚至不支持语音。不过,打字还是比较丝滑流畅的。      

    Round 2

     

          

    讯飞:可下载离线语音包,不过在正常网络通畅情况下,语音识别的速度和准确性还是相当高的。

    Round 3

    百度:也可下载离线语音,无网络连接状态下,语音识别效果还是可以的。

    不知国内经常使用讯飞、百度输入法的小伙伴们,看到这一消息有何想法?欢迎留言。

    参考:https://ai.googleblog.com/2019/03/an-all-neural-on-device-speech.html

    (本文为AI科技大本营原创文章,转载请微信联系 1092722531)

    展开全文
  • 用java调用科大讯飞的离线语音识别dll实现离线识别(JNA实现) 本人接的任务,做离线语音识别,用的是科大讯飞的离线识别,java不支持离线了,所以下载了windows的离线包,用JNA进行调用。之前用的是jni,但是一直...
  • 离线语音识别Demo

    2020-07-11 20:55:51
    离线语音识别Demo
  • 讯飞离线语音识别.zip

    2020-06-29 12:40:41
    基于android的离线语音识别,实现没有网络的情况下把语音转换成文字信息。 对程序内容进行了简化,内容简单明了,容易看懂。 包含了离线语音需要的所有离线包 speechapp.java文件第22行修改成自己在讯飞网站申请的id...
  • C# 开发的离线语音识别,语音转文字,可以识别短语音,指定识别库,程序已测试运行成功,识别速度快,需要的可以自行修改使用
  • 基于android的离线语音识别,实现没有网络的情况下把语音转换成文字信息。 对程序内容进行了简化,内容简单明了,容易看懂。 包含了离线语音需要的所有离线包 speechapp.java文件第22行修改成自己在讯飞网站申请的id...
  • 基于android的离线语音识别,实现没有网络的情况下把语音转换成文字信息。 对程序内容进行了简化,内容简单明了,容易看懂。 包含了离线语音需要的所有离线包
  • 博文配套的源码,初稿,用java调用科大讯飞的离线语音识别dll实现离线识别,只要路径对了,就绝对能运行,跟机型无关,操作系统windows
  • 关于Android的离线语音识别的技术,遍观若干厂家唯有科大讯飞和百度有离线的语音识别功能,但是科大讯飞是收费的,百度的离线语音识别就是关键词唤醒,但是只接受3个自定义的唤醒词,而且必须经过评定3分以上唤醒词...
  • 关于语音识别,国内已经有比较好的公司推出相关的服务了,比如百度免费的离在线融合语音识别以及讯飞收费的在线和离线语音识别服务。这里不作过多介绍,需要的同学可以直接去官网阅读接入文档。这里要介绍的是一个...
  • 前言:因为在学校要做语音相关的开发,并且应用环境多数是在无网络状态下,故要用到离线语音识别。经过一番查阅,发现目前在做离线语音识别的,尤其是android环境下的离线语音识别的人确实很少。(插一句如果想要...
  • Android平台下PocketSphinx离线语音识别最完整讲义!
  • linux离线语音识别

    2020-06-30 17:55:32
    是一个linux下的离线语音识别, 包含win64, X86等so库文件
  • 首先本文要说的两个前提1.android平台离线语音识别 2.小范围语音 小范围语音指的是相对固定的命令。本文的例子大概实现了20条语音命令,超出范围的无法识别。因此本文中离线语音的使用范围也有限,对于一
  • Ubuntu16.04实现Sphinx离线语音识别   版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/boke14122621/article/details/79871224 自带Python2.7或3.0+版本都可以 使用的是3.5编译 ...
1 2 3 4 5 ... 20
收藏数 7,389
精华内容 2,955