慢牛系列五:用百度語音識別添加自選股


慢牛系列五:用百度語音識別添加自選股

開發慢牛股票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

歡迎安裝試用!

最后,歡迎園友提出好的想法,評論留名!謝謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM