• 慢牛系列五:用百度语音识别添加自选股


    慢牛系列五:用百度语音识别添加自选股

    开发慢牛股票app已经有很长一段时间,最近在考虑用什么方式添加自选股最方便?传统的做法是为用户专门开发一个键盘,字母或者数字的,帮助用户录入股票的简称或者编码,大多数app都是这么做的,下面是海通证券app为用户开发的键盘,多数券商是这么做的,这么做已经很不错了。

    当然,我的想法没有什么太突出的,无外乎三种办法:

    • 第一种:从其他APP导入,比如从腾讯自选股导入,要获得腾讯自选股数据,需要用QQ号登陆QQ的Web端,一般不会有人愿意在我这里输入QQ和密码的。
    • 第二种:语音识别,上网搜了下,主要是百度和科大讯飞提供语音识别SDK,而且免费的,下载了demo,感觉不错,可以做。
    • 第三种:拍照,用户只需拍下来QQ自选股,上传图片,后台分析出个股,添加到用户的自选股里,上网搜了下图片识别的SDK,百度有,但是只有企业版,收费的,图片识别这块有个开源项目:Tessercat,出自惠普实验室,目前是谷歌在维护,试用了下,对简体中文的识别项目不太理想,如果想识别高的话,需要进行中文的识别训练,当然也尝试了下,是个体力活,所以这方面还是等百度开放他们的图片识别API在做吧,或者园里的大牛们有什么好的方案推荐下,多谢啦!

    下面说说我使用百度语音识别的效果:

    百度语音添加自选股

    实现过程如下:

    1.注册百度语音

    注册网址:百度语音注册

    开发者需要有个百度开发平台账号,还需要在应用中创建一个应用,并为应用开通语音识别服务,开通后获得api_key和secret_key。

    下载SDK,我使用的是在线SDK。

    在线SDK
    我的应用是需要再有网络情况下使用的,所以选择在线的SDK。

    2.引用文件

    将SDK中的libs和res有两个文件夹拷贝到android项目对应的目录下,可以拷贝到android的主项目下,也可以专门建立子项目,放置这些文件。我单独建立了一个android项目,然后主项目引用子项目。

    官方的手册里只是说明了如何在Eclipse里如何引用,可以参考这篇文章链接:
    Eclipse中使用百度语音

    我项目里用的是Android Studio,build工具是gradle,主要是gradle的设置。

    
    apply plugin: 'com.android.library'
    
    android {
    	……
        sourceSets{
            main {
                jniLibs.srcDir(['libs']) // <-- Set your folder here!
            }
        }
    	……
    }
    
    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
    	……
    }
    
    

    说明:
    SDK中包含jar和so文件,jar可以通过dependencies里设置引用:

    compile fileTree(dir: 'libs', include: ['*.jar'])

    so文需要再上面s件ourceSets中设置,如上面的main的设置:

    jniLibs.srcDir(['libs'])

    注意:还需要在libs下复制一个文件夹armeabi,重命名:armeabi-v7a,如果不加个这个文件夹,会报一个找不到BDVoiceRecognitionClient_MFE_V1类型的异常。

    3.接入

    语音主要有两种模式,对话框模式和API模式,我这里是说下对话框模式,API模式参考后续的代码。
    关于如何调用语音对话框,百度语音官方的文档里有说明,因为我要在Reac Native里使用百度语音,所以需要写桥接代码,下面是在android端的代码,继承ViewGroupManager,在React Native里做了一个容器。

    
    public class VoiseRecognitionManager extends ViewGroupManager<ReactViewGroup> {
        public static final int COMMAND_SHOW = 0;
        public static final int COMMAND_DISMISS = 1;
        private static final String CLASS_NAME = "VoiseRecognition";
        private ReactViewGroup view=null;
        private BaiduASRDigitalDialog mDialog=null;
    
        @Override
        public String getName() {
            return CLASS_NAME;
        }
    
        @Override
        protected ReactViewGroup createViewInstance(ThemedReactContext reactContext) {
            this.view= new ReactViewGroup(reactContext);
            return this.view;
        }
    
        @Override
        public @Nullable Map<String, Integer> getCommandsMap() {
            return MapBuilder.of(
                    "show",
                    COMMAND_SHOW,
                    "dismiss",
                    COMMAND_DISMISS);
        }
    
        @Override
        public void receiveCommand(
                ReactViewGroup view,
                int commandId,
                @Nullable ReadableArray config) {
                if(commandId==COMMAND_SHOW){
                    this.showDialog(view.getContext(),config.getMap(0));
                }else if(commandId==COMMAND_DISMISS){
                    this.dismiss();
                }
        }
        @ReactMethod
        public void showDialog(Context context,ReadableMap config) {
            if (mDialog != null) {
                mDialog.dismiss();
            }
            Bundle params=new Bundle();
            String api_key=config.getString("api_key");
            String secret_key=config.getString("secret_key");
            //设置开放平台 API Key
            params.putString(BaiduASRDigitalDialog.PARAM_API_KEY,api_key);
            //设置开放平台 Secret Key
            params.putString(BaiduASRDigitalDialog.PARAM_SECRET_KEY,secret_key);
    
            //设置识别领域:搜索、输入、地图、音乐……,可选。默认为输入。
            Object PARAM_PROP_VALUE= config.getInt("prop");
            params.putInt( BaiduASRDigitalDialog.PARAM_PROP, PARAM_PROP_VALUE==null?VoiceRecognitionConfig.PROP_INPUT:(Integer) PARAM_PROP_VALUE);
    
            //设置语种类型:中文普通话,中文粤语,英文,可选。默认为中文普通话
            Object LANGUAGE=config.getString("language");
            params.putString( BaiduASRDigitalDialog.PARAM_LANGUAGE,LANGUAGE==null?VoiceRecognitionConfig.LANGUAGE_CHINESE:LANGUAGE.toString());
            //如果需要语义解析,设置下方参数。领域为输入不支持
            Object PARAM_NLU_ENABLE=config.getBoolean("nlu_enable");
            params.putBoolean(BaiduASRDigitalDialog.PARAM_NLU_ENABLE,PARAM_NLU_ENABLE==null?false:(boolean)PARAM_NLU_ENABLE);
            // 设置对话框主题,可选。BaiduASRDigitalDialog 提供了蓝、暗、红、绿、橙四中颜色,每种颜
            //        色又分亮、暗两种色调。共 8 种主题,开发者可以按需选择,取值参考 BaiduASRDigitalDialog 中
            //        前缀为 THEME_的常量。默认为亮蓝色
            Object PARAM_DIALOG_THEME=config.getInt("dialog_theme");
            params.putInt(BaiduASRDigitalDialog.PARAM_DIALOG_THEME,PARAM_DIALOG_THEME==null?BaiduASRDigitalDialog.THEME_RED_DEEPBG:(Integer) PARAM_DIALOG_THEME);
    
            params.putBoolean( BaiduASRDigitalDialog.PARAM_START_TONE_ENABLE,true);
            params.putBoolean( BaiduASRDigitalDialog.PARAM_END_TONE_ENABLE,true);
            params.putBoolean( BaiduASRDigitalDialog.PARAM_TIPS_TONE_ENABLE,true);
            mDialog=new BaiduASRDigitalDialog(context,params);
            final ThemedReactContext reactContext=(ThemedReactContext)context;
            final ReactViewGroup tempView=this.view;
            DialogRecognitionListener mRecognitionListener=new DialogRecognitionListener(){
    
                @Override
                public void onResults(Bundle results){
                    //此处处理识别结果,识别结果可能有多个,按置信度从高到低排列,第一个元素是置信度最高的结果。
    
                    ArrayList<String> rs=results !=null?results
                            .getStringArrayList(RESULTS_RECOGNITION):null;
                    if(rs!=null){
                        WritableArray params=Arguments.createArray();
                        for (String r : rs) {
                            params.pushString(r);
                        }
                        WritableMap data=Arguments.createMap();
                        data.putArray("result",params);
    					//向JS端发送事件,附带识别结果
                        reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
                                tempView.getId(),
                                "topChange",
                                data);
                    }
                }
            };
            mDialog.setDialogRecognitionListener(mRecognitionListener);
            mDialog.show();
        }
        @ReactMethod
        public void dismiss(){
            if(this.mDialog!=null)
                this.mDialog.dismiss();
        }
    }
    
    
    

    JS端代码

    
    var requireNativeComponent = require('requireNativeComponent');
    var React = require('React');
    var View = require('View');
    var PropTypes = require('ReactPropTypes');
    var UIManager = require('UIManager');
    var TouchableOpacity=require('TouchableOpacity');
    var VOISE_REF = 'baiduVoise';
    var params=require('./Params')
    
    var BaiduVoise = React.createClass({
        getDefaultProps: function() {
          return {
            prop:params.PROP_FINANCE,
            language:params.LANGUAGE_CHINESE,
            dialog_theme:params.THEME_RED_DEEPBG,
            nlu_enable:false,
          };
        },
        propTypes: {
          ...View.propTypes,
          api_key: PropTypes.string,
          secret_key: PropTypes.string,
          prop: PropTypes.number,
          language: PropTypes.string,
          dialog_theme: PropTypes.number,
        },
        show: function(callback) {
          var config = {
            api_key: this.props.api_key,
            secret_key: this.props.secret_key,
            prop: this.props.prop,
            language: this.props.language,
            dialog_theme: this.props.dialog_theme,
            nlu_enable: this.props.nlu_enable
          };
          //向原生端发送事件,启动对话框
          UIManager.dispatchViewManagerCommand(
            React.findNodeHandle(this.refs[VOISE_REF]),
            UIManager.VoiseRecognition.Commands.show,
            [config]);
        },
        hide: function() {
    	  //向原生端发送事件,隐藏对话框
          UIManager.dispatchViewManagerCommand(
            React.findNodeHandle(this.refs[VOISE_REF]),
            UIManager.VoiseRecognition.Commands.dismiss,
            []);
        },
        onReceive: function(e) {
          var result=e.nativeEvent.result;
          if(this.props.nlu_enable){
            var str=e.nativeEvent.result;
            var obj=JSON.parse(str);
            result=obj.item;
          }
          this.props.onReceive&&this.props.onReceive(result);
        },
        onPress:function (argument) {
          var me=this
          me.show();
        },
        render: function() {
          return (  
            <TouchableOpacity
                activeOpacity ={0.5}       
                underlayColor="#B5B5B5"
                onPress={this.onPress}>
                <VoiseRecognition
                  ref={VOISE_REF}
                  onChange={this.onReceive}
                  style= {this.props.style}> 
                  {this.props.children}
                </VoiseRecognition>
            </TouchableOpacity>
          );
        },
    
    });
    
    BaiduVoise.Params=params;
    
    var VoiseRecognition = requireNativeComponent('VoiseRecognition', BaiduVoise, {
      nativeOnly: {
        onChange: true
      }
    });
    
    
    module.exports = BaiduVoise;
    
    
    

    4.使用方法

    /* @flow */
    'use strict';
    
    var React = require('react-native');
    var {
      BaiduVoise,
      SpeechRecognizer
    }=require('react-native-voise');
    
    var {
      StyleSheet,
      View,
      Text
    } = React;
    
    var Component = React.createClass({
        getInitialState() {
            return { result:'' }
        },
        onReceive:function (results) {
            //results is a list ,the first one is the best result.
            this.setState((state)=>{
              state.result=results[0];
            });
        },
        render: function() {
            return (
                <View style={styles.container}>
                    <Text>this.state.result</Text>
                    <BaiduVoise 
                      ref={'BaiduVoise'}
                      style={styles.button}
                      api_key={'q0UcNM0glvjekMtBQNWzM92y'} //设置api_key和secret_key
                      secret_key={'8hRsMQCQGNdwqnyF8GkWBgr6WObZFT5l'} 
                      onReceive={this.onReceive}>      
                        <Text>点击,说话</Text>
                    </BaiduVoise>
                </View>
            );
        }
    });
    
    var styles = StyleSheet.create({
        container:{
            flex:1
        },
        button:{
            height:50,
        }
    });
    
    
    module.exports = Component;
    
    

    在React Native里,桥接原生组件是一件比较简单的事情,只要学一些原生的东西就能做了,主要是React Native给我们提供了平台和工具集,平台负责管理组件的布局,样式,行为,又建立了原生和JS端通信机制,让我们做Web的同学也可以做原生了,当然,特殊情况下还是需要完全原生的,大多数小而美的app,React Native是非常合适的。

    听说微信要做应用号,将来微信估计也要做这样的一个平台,微信提供标准的UI组件,众多app开发者可以在微信的平台上通过js写出性能体验更好的app了,相比现在通过浏览器实现的企业号,体验肯定会更好。

    如果有想体验百度语音的用户,可以下载慢牛APP的APK体验,点击右上角的放大镜,进入自选股添加页面:

    关注慢牛的公众号:发送react,返回apk下载链接,apk大小8M,最好连接WiFi下载。

    这个项目的代码已经发布到github上:https://github.com/hongyin163/react-native-voise

    欢迎安装试用!

    最后,欢迎园友提出好的想法,评论留名!谢谢!

  • 相关阅读:
    【Flutter学习】之绘画实例(二)
    【Flutter学习】之绘画实例(一)
    【Flutter学习】之动画实现原理浅析(三)
    【Flutter学习】之动画实现原理浅析(二)
    NopCommerce支持多种类型的数据库
    NopCommerce 多数据库方案
    开发程序需要注意的点
    基于SVN的项目管理——集中与分散
    JS代码优化小技巧
    开发日记:中控PUSH协议
  • 原文地址:https://www.cnblogs.com/hongyin163/p/react-voise.html
Copyright © 2020-2023  润新知