天天看點

語音識别,語義了解一站式解決(android平台&olami sdk)

轉載請注明CSDN博文位址:http://blog.csdn.net/ls0609/article/details/71519203

語音記賬demo:http://blog.csdn.net/ls0609/article/details/72765789

olami sdk實作了把錄音或者文字轉化為使用者可以了解的json字元串進而實作語義了解,使用者可以定義自己的語義,是不是很強大?本文講述怎麼自定義語義,以及如何解析自定義語義。
本文使用olami sdk做了一個線上聽書的demo,使用者隻需類似“我想聽***”就能實作聽書的線上查找并播放。用的是喜馬拉雅的線上聽書sdk.基于eclipse開發環境,libs目錄下jar和so檔案如下:

olami-android-sdk.jar //olami sdk 的jar
afinal_0.5.1_bin.jar
litepal.jar
gson-2.2.4.jar
okhttp-2.4.0.jar
okhttp-urlconnection-2.2.0.jar
okio-1.4.0.jar
opensdk.jar          //上面這幾個都是喜馬拉雅需要的jar
libspeex.so          //olami sdk 需要用到speex壓縮功能
libxmediaplayer.so   // 喜馬拉雅so
libxmediaplayer_x.so // 喜馬拉雅so
           

概述:

VoiceSdkService中定義了OlamiVoiceRecognizer語音識别引擎,通過點選MusicActivity的開始button啟動錄音,錄音結果在VoiceSdkService中的onResult()回調中拿到識别的Json字元串,在processServiceMessage()函數中處理後找到要聽書的名稱,然後進入BookUtil進行搜尋,搜尋到結果後通知VoiceSdkService進行播放,并通知MusicActivity更新播放進度等資訊。

1.AndroidManifest.xml配置

<?xml version="1.0" encoding="utf-8"?>
     <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.olami.musicdemo"
        android:versionCode="1"
        android:versionName="1.0" >

        <uses-sdk
            android:minSdkVersion="8"
            android:targetSdkVersion="21" />

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

        <application
            android:name="com.olami.musicdemo.OlamiApplication"
            android:allowBackup="true"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme" >

            <!--喜馬拉雅聽書測試賬号app_key-->
            <meta-data
                android:name="app_key"
                android:value="b617866c20482d133d5de66fceb37da3" />
            <!--喜馬拉雅聽書測試賬号包名-->
            <meta-data
                android:name="pack_id"
                android:value="com.app.test.android" />

            <activity
                android:name=".MusicActivity"
                android:label="@string/app_name" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />

                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>

             <!--注冊olami sdk service-->
            <service
                android:name=".VoiceSdkService"
                android:exported="true" >
            </service>

            <!--注冊喜馬拉雅聽書service-->
             <service                
              android:name=
             "com.ximalaya.ting.android.opensdk.player.service.XmPlayerService"
            />
        </application>

    </manifest>
           

2.layout布局檔案

layout_musicview.xml

TextView 有兩個,tv_name顯示聽書的名稱, tv_totoal_time顯示聽書的總時間。

ProgressBar 實時重新整理顯示聽書的進度

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:background="@android:color/transparent">


    <TextView
        android:text="name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:layout_centerHorizontal="true"
        android:id="@+id/tv_name"/>

    <ProgressBar
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/tv_name"
        android:layout_marginTop="10dp"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:id="@+id/progressbar_music"/>

    <TextView
        android:text="total_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/progressbar_music"
        android:layout_marginTop="10dp"
        android:layout_centerHorizontal="true"
        android:id="@+id/tv_total_time"/>

</RelativeLayout>
           

activity_music.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    tools:context="com.olami.musicdemo.MusicActivity" >

    <TextView
        android:id="@+id/tv_inputText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:text="輸入:" />

    <TextView
        android:id="@+id/tv_volume"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/tv_inputText"
        android:layout_below="@+id/tv_inputText"
        android:layout_marginTop="40dp"
        android:text="音量:" />

    <TextView
        android:id="@+id/tv_result"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/tv_volume"
        android:layout_marginTop="20dp"
        android:maxLines="15"
        android:ellipsize="end"
        android:text="伺服器傳回sentence:"
        android:visibility="visible" />

    <com.olami.musicdemo.MusicView
        android:id="@+id/music_view"
        android:layout_width="fill_parent"
        android:layout_height="80dp"
        android:layout_centerInParent="true"
        >
    </com.olami.musicdemo.MusicView>

    <EditText
        android:id="@+id/et_content"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:layout_above="@+id/btn_stop"
        android:layout_alignLeft="@+id/tv_inputText"
        android:layout_marginBottom="10dp"
        android:layout_toLeftOf="@+id/btn_send"
        android:background="#E7E7E7"
        android:singleLine="true"
        android:text="上海的天氣" />    

     <Button
        android:id="@+id/btn_send"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/et_content"
        android:layout_alignBottom="@+id/et_content"
        android:layout_alignParentRight="true"
        android:text="送出" />

    <Button
        android:id="@+id/btn_start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"  
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:text="開始" />

    <Button
        android:id="@+id/btn_stop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/et_content"
        android:layout_alignParentBottom="true"
        android:text="停止" />

    <Button
        android:id="@+id/btn_cancel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_toRightOf="@+id/et_content"
        android:text="取消" />

</RelativeLayout>
           

自定義MusicView比較簡單,代碼如下:

public class MusicView extends RelativeLayout{
    private Context mContext;
    private Handler mHandler;
    private TextView mTextViewName;
    private TextView mTextViewTotalTime;
    private ProgressBar mProgressBar;
    public MusicView(Context context,AttributeSet attrs) {
        super(context,attrs);
        LayoutInflater inflater =(LayoutInflater) context.getSystemService(
                                            context.LAYOUT_INFLATER_SERVICE);
        RelativeLayout view = (RelativeLayout) inflater.inflate(R.layout.layout_musicview, this,true);
        mTextViewName = (TextView) view.findViewById(R.id.tv_name);
        mTextViewTotalTime = (TextView) view.findViewById(R.id.tv_total_time);
        mProgressBar = (ProgressBar)view.findViewById(R.id.progressbar_music);

    }

    public void initMusicView(Context context,Handler handler)
    {
        mContext = context;
        mHandler = handler;
    }

    public void setMusicName(String name)
    {//設定播放名稱
        mTextViewName.setText(name);
    }

    public void setProgress(int progress)
    {//設定播放進度
        mProgressBar.setProgress(progress);
    }

    public void setTotalTime(String time)
    {//設定播放總時間
        mTextViewTotalTime.setText(time);
    }

}
           

布局效果圖如下:

語音識别,語義了解一站式解決(android平台&amp;olami sdk)

3.MusicActivity和VoiceSdkService通信

本文沒有用bind service的方式實作activity和service的消息通信。

MusicAcitity 和 VoiceSdkService中分别實作了一個CommunicationAssist的接口

public interface CommunicationAssist {
    public void callBack(int what, int arg1, int arg2,Bundle data, Object obj);
}
           

然後把他們分别實作CommunicationAssist接口的變量注冊到OlamiApplication,這樣通過OlamiApplication實作了MusicAcitity 和 VoiceSdkService橋接。

3.1OlamiApplication

1) 注冊MusicActivity到VoiceSdkService的回調

public void setActivityToServiceListener(CommunicationAssist listener)
{
   ActivityToServiceListener = listener;
}
           

這個是在VoiceSdkService中調用setActivityToServiceListener(),把VoiceSdkService中的VoiceSdkComAssist注冊到application中,MusicActivity中可以通過getActivityToServiceListener

這個函數回調向VoiceSdkService發送消息。

2) 注冊 VoiceSdkService到MusicActivity回調

public void setServiceToActivityListener(CommunicationAssist listener)
{
  mServiceToActivityListener = listener;
}
           

這個是在MusicAcitivity中調用setServiceToActivityListener(),這樣在VoiceSdkService中就可以通過getServiceToActivityListener()獲得回調向MusciActivity發送消息。

3.2 MusicActivity

public class MusicActivity extends Activity {
    private Handler mHandler;
    private Handler mInComingHandler;
    private ActivityComAssist mActivityComAssist;
    private Button mBtnStart;
    private Button mBtnStop;
    private Button mBtnCancel;
    private Button mBtnSend;
    private EditText mEditText;
    private TextView mTextView;
    private TextView mInputTextView;
    private TextView mTextViewVolume;
    private BookUtil mBookUtil = null;
    private MusicView mMusicView = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_music);
        initHandler();//初始化handler用于内部消息處理
        initInComingHandler();//用于處理來自VoiceSdkService的消息
        initCommunicationAssist();//向application注冊消息回調,VoiceSdkSerive可以
                //通過getServiceToActivityListener()獲得回調向MusicActivity發送消息
        initView();//初始化view控件
        Intent intent = new Intent();
        intent.setClass(MusicActivity.this, VoiceSdkService.class);
        startService(intent);//啟動背景服務

    }

    private void initView()
    {
        mBtnStart = (Button) findViewById(R.id.btn_start);
        mBtnStop = (Button) findViewById(R.id.btn_stop);
        mBtnCancel = (Button) findViewById(R.id.btn_cancel);
        mBtnSend = (Button) findViewById(R.id.btn_send);
        mInputTextView = (TextView) findViewById(R.id.tv_inputText);
        mEditText = (EditText) findViewById(R.id.et_content);
        mTextView = (TextView) findViewById(R.id.tv_result);
        mTextViewVolume = (TextView) findViewById(R.id.tv_volume);

        mBtnStart.setOnClickListener(new OnClickListener(){

            @Override
            public void onClick(View v) {
                sendMessageToService(MessageConst.CLIENT_ACTION_START_RECORED,,,null,null);
            }           
        });

        mBtnStop.setOnClickListener(new OnClickListener(){

            @Override
            public void onClick(View v) {
                sendMessageToService(MessageConst.CLIENT_ACTION_STOP_RECORED,,,null,null);
                mBtnStart.setText("開始");
                Log.i("led","MusicActivity mBtnStop onclick 開始");
            }           
        });

        mBtnCancel.setOnClickListener(new OnClickListener(){

            @Override
            public void onClick(View v) {
                sendMessageToService(MessageConst.CLIENT_ACTION_CANCEL_RECORED,,,null,null);
            }           
        });

        mBtnSend.setOnClickListener(new OnClickListener(){

            @Override
                public void onClick(View v) {
            sendMessageToService(
            MessageConst.CLIENT_ACTION_SENT_TEXT,,,null,mEditText.getText());
                mInputTextView.setText("文字: "+mEditText.getText());
            }           
        });

        mMusicView = (MusicView) findViewById(R.id.music_view);
        //if(mMusicView != null)
            //mMusicView.initMusicView(MusicActivity.this,mHandler);

    }

    private void initHandler()
    {
        mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg)
            {
                switch (msg.what){
                case MessageConst.CLIENT_ACTION_START_RECORED:
                    break;
                default:
                    break;  
                }
            }
        };
    }
    //InComingHandler 收到來自VoiceSdkService的消息用于更新界面,
    //包括開始錄音,結束錄音,播放的書的名稱和進度,音量等資訊。
    private void initInComingHandler()
    {
        mInComingHandler = new Handler(){
            @Override
            public void handleMessage(Message msg)
            {
                switch (msg.what){
                case MessageConst.CLIENT_ACTION_START_RECORED:
                    mBtnStart.setText("錄音中");
                    Log.i("led","MusicActivity 錄音中");
                    break;
                case MessageConst.CLIENT_ACTION_STOP_RECORED:
                    mBtnStart.setText("識别中");
                    Log.i("led","MusicActivity 識别中");
                    break;
                case MessageConst.CLIENT_ACTION_CANCEL_RECORED:
                    mBtnStart.setText("開始");
                    mTextView.setText("已取消");
                    break;
                case MessageConst.CLIENT_ACTION_ON_ERROR:
                    mTextView.setText("錯誤代碼:"+msg.arg1);
                    mBtnStart.setText("開始");
                    break;
                case MessageConst.CLIENT_ACTION_UPDATA_VOLUME:
                    mTextViewVolume.setText("音量: "+msg.arg1);
                    break;
                case MessageConst.SERVER_ACTION_RETURN_RESULT:
                    //mTextView.setText(msg.obj.toString());
                    mBtnStart.setText("開始");
                    break;
                case MessageConst.CLIENT_ACTION_PLAY_BOOK_AFTER_SEARCH:
                    mBtnStart.setText("開始");
                    mBookUtil = BookUtil.getInstance();
                    mBookUtil.play(msg.arg1);
                    break;
                case MessageConst.CLIENT_ACTION_UPDATA_PLAYING_BOOK_NAME:
                    mMusicView.setMusicName(msg.obj.toString());
                    break;
                case MessageConst.CLIENT_ACTION_UPDATE_BOOK_PROGRESS:
                    int current = msg.arg1;
                    int duration = msg.arg2;
                    mMusicView.setProgress(current*/duration);
                    float time = duration//;
                    mMusicView.setTotalTime("總時間:"+time);
                    break;
                case MessageConst.CLIENT_ACTION_UPDATA_INPUT_TEXT:
                    if(msg.obj != null)
                       mInputTextView.setText("文字: "+msg.obj.toString());
                    break;
                case MessageConst.CLIENT_ACTION_UPDATA_SERVER_MESSAGE:
                    if(msg.obj != null)
                        mTextView.setText("伺服器傳回sentence: "+msg.obj.toString());
                    break;
                default:
                    break;
                }
            }
        };
    }

    private void initCommunicationAssist()
    {//向Application注冊VoiceSdkService到MusicActivity的回調
        mActivityComAssist = new ActivityComAssist();
        OlamiApplication.getInstance().setServiceToActivityListener(mActivityComAssist);
    }

    private void sendMessageToService(int what, int arg1, int arg2, Bundle data, Object obj)
    {//向VoiceSdkService發送消息
        if(OlamiApplication.getInstance().getActivityToServiceListener() != null)
            OlamiApplication.getInstance().getActivityToServiceListener().callBack
            (what, arg1, arg2, data, obj);
    }

    private class ActivityComAssist implements CommunicationAssist{
    //實作CommunicationAssist借口,用于回調VoiceSdkService發送過來的消息
        @Override
        public void callBack(int what, int arg1, int arg2, Bundle data,Object obj) {
            Message msg = Message.obtain(null, what);
            msg.arg1 = arg1;
            msg.arg2 = arg2;
            if (data != null)
                msg.setData(data);
            if (obj != null)
                msg.obj = obj;
            mInComingHandler.sendMessage(msg);
        }       
    }

    @Override
    public void onDestroy() {
        //退出應用,停止VoiceSdkService,會進行資源的釋放
        super.onDestroy();
        Intent intent = new Intent();
        intent.setClass(MusicActivity.this, VoiceSdkService.class);
        stopService(intent);
    }
}
           

3.3 VoiceSdkService

@Override
    public void onCreate() {
        initHandler();//用于内部消息處理
        initInComingHandler();//用于處理來自MusicActivity的消息
        initCommunicationAssist();//向application注冊消息回調,這樣MusicActivity可
        //以通過getActivityToServiceListener()回調向VoiceSdkService發送消息
        initViaVoiceRecognizerListener();//初始化錄音識别回調listener
        init();//olami錄音識别引擎初始化
        initXmly();//喜馬拉雅初始化
    }
           
public void init()
{
    initHandler();
    mOlamiVoiceRecognizer = new OlamiVoiceRecognizer(VoiceSdkService.this);
    TelephonyManager telephonyManager=(TelephonyManager) this.getSystemService(
    (this.getBaseContext().TELEPHONY_SERVICE);
    String imei=telephonyManager.getDeviceId();
    mOlamiVoiceRecognizer.init(imei);//設定身份辨別,可以填null

    mOlamiVoiceRecognizer.setListener(mOlamiVoiceRecognizerListener);//設定識别結果回調listener
    mOlamiVoiceRecognizer.setLocalization(
    OlamiVoiceRecognizer.LANGUAGE_SIMPLIFIED_CHINESE);//設定支援的語音類型,優先選擇中文簡體
    mOlamiVoiceRecognizer.setAuthorization("51a4bb56ba954655a4fc834bfdc46af1",
                            "asr","68bff251789b426896e70e888f919a6d","nli");  
    //注冊Appkey,在olami官網注冊應用後生成的appkey
    //注冊api,請直接填寫“asr”,辨別語音識别類型
    //注冊secret,在olami官網注冊應用後生成的secret
    //注冊seq ,請填寫“nli”

    mOlamiVoiceRecognizer.setVADTailTimeout();//錄音時尾音結束時間,建議填//2000ms
    //設定經緯度資訊,不願上傳位置資訊,可以填0 
    mOlamiVoiceRecognizer.setLatitudeAndLongitude(,); 
}
           

定義OlamiVoiceRecognizerListener

onError(int errCode)//出錯回調,可以對比官方文檔錯誤碼看是什麼錯誤

onEndOfSpeech()//錄音結束

onBeginningOfSpeech()//錄音開始

onResult(String result, int type)//result是識别結果JSON字元串

onCancel()//取消識别,不會再傳回識别結果

onUpdateVolume(int volume)//錄音時的音量,1-12個級别大小音量

下面是VoiceSdkService完整代碼:

public class VoiceSdkService extends Service{

    private Handler mHandler;
    private Handler mInComingHandler;
    private VoiceSdkComAssist mVoiceSdkComAssist;
    private OlamiVoiceRecognizer mOlamiVoiceRecognizer;
    private OlamiVoiceRecognizerListener mOlamiVoiceRecognizerListener;
    private BookUtil mBookUtil = null;
    private boolean mIsRecordPause = false;

    @Override
    public void onCreate() {
        initHandler();
        initInComingHandler();
        initCommunicationAssist();
        initViaVoiceRecognizerListener();
        init();
        initXmly();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }

    public void init()
    {
        initHandler();
        mOlamiVoiceRecognizer = new OlamiVoiceRecognizer(VoiceSdkService.this);
        TelephonyManager telephonyManager=(TelephonyManager) this.getSystemService(
        this.getBaseContext().TELEPHONY_SERVICE);
        String imei=telephonyManager.getDeviceId();
        mOlamiVoiceRecognizer.init(imei);//set null if you do not want to notify olami server.

        mOlamiVoiceRecognizer.setListener(mOlamiVoiceRecognizerListener);
        mOlamiVoiceRecognizer.setLocalization(
                                         OlamiVoiceRecognizer.LANGUAGE_SIMPLIFIED_CHINESE);
        mOlamiVoiceRecognizer.setAuthorization(
            "51a4bb56ba954655a4fc834bfdc46af1",
            "asr","68bff251789b426896e70e888f919a6d","nli");        
        mOlamiVoiceRecognizer.setVADTailTimeout();
        mOlamiVoiceRecognizer.setLatitudeAndLongitude(,); 
    }

    private void initHandler()
    {
        mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg)
            {
                switch (msg.what){
                case MessageConst.CLIENT_ACTION_START_RECORED:
                    sendMessageToActivity(MessageConst.CLIENT_ACTION_START_RECORED,,,null,null);
                    break;
                case MessageConst.CLIENT_ACTION_STOP_RECORED:
                    sendMessageToActivity(MessageConst.CLIENT_ACTION_STOP_RECORED,,,null,null);
                    break;
                case MessageConst.CLIENT_ACTION_ON_ERROR:
                    sendMessageToActivity(MessageConst.CLIENT_ACTION_ON_ERROR,msg.arg1,,null,null);
                    break;
                case MessageConst.CLIENT_ACTION_PLAY_BOOK_AFTER_SEARCH:
                    sendMessageToActivity(MessageConst.
                    CLIENT_ACTION_PLAY_BOOK_AFTER_SEARCH, msg.arg1, , null, msg.obj);
                    break;
                case MessageConst.CLIENT_ACTION_UPDATA_PLAYING_BOOK_NAME:
                    sendMessageToActivity(MessageConst.
                    CLIENT_ACTION_UPDATA_PLAYING_BOOK_NAME, msg.arg1, , null, msg.obj);
                    break;
                case MessageConst.CLIENT_ACTION_UPDATE_BOOK_PROGRESS:
                    sendMessageToActivity(MessageConst.
                    CLIENT_ACTION_UPDATE_BOOK_PROGRESS, msg.arg1, msg.arg2, null, null);
                    break;
                case MessageConst.CLIENT_ACTION_CANCEL_RECORED:
                    sendMessageToActivity(MessageConst.
                    CLIENT_ACTION_CANCEL_RECORED, msg.arg1, msg.arg2, null, null);
                    break;
                default:
                    break;
                }
            }
        };
    }

    private void initInComingHandler()
    {
        mInComingHandler = new Handler(){
            @Override
            public void handleMessage(Message msg)
            {
                switch (msg.what){
                case MessageConst.CLIENT_ACTION_START_RECORED:
                    if(mOlamiVoiceRecognizer != null)
                        mOlamiVoiceRecognizer.start();  
                    break;
                case MessageConst.CLIENT_ACTION_STOP_RECORED:
                    if(mOlamiVoiceRecognizer != null)
                        mOlamiVoiceRecognizer.stop();   
                    break;
                case MessageConst.CLIENT_ACTION_CANCEL_RECORED:
                    if(mOlamiVoiceRecognizer != null)
                        mOlamiVoiceRecognizer.cancel(); 
                    break;
                case MessageConst.CLIENT_ACTION_SENT_TEXT:
                    if(mOlamiVoiceRecognizer != null)
                        mOlamiVoiceRecognizer.sendText(msg.obj.toString());                 
                    break;
                }
            }
        };
    }

    private void initViaVoiceRecognizerListener()
    {
        mOlamiVoiceRecognizerListener = new OlamiVoiceRecognizerListener();
    }

    private class OlamiVoiceRecognizerListener implements IOlamiVoiceRecognizerListener{

        @Override
        public void onError(int errCode) {
            mHandler.sendMessage(mHandler.obtainMessage(
            MessageConst.CLIENT_ACTION_ON_ERROR,errCode,));

        }

        @Override
        public void onEndOfSpeech() {
            mHandler.sendEmptyMessage(MessageConst.CLIENT_ACTION_STOP_RECORED);
            if(mIsRecordPause)
            {
                mIsRecordPause = false;
                mBookUtil.resumePlay();
            }

        }

        @Override
        public void onBeginningOfSpeech() {
            if(mBookUtil.isPlaying())
            {
                mBookUtil.pause();
                mIsRecordPause = true;
            }
            mHandler.sendEmptyMessage(MessageConst.CLIENT_ACTION_START_RECORED);

        }

        @Override
        public void onResult(String result, int type) {     
            sendMessageToActivity(MessageConst.SERVER_ACTION_RETURN_RESULT,type,,null,result);
            processServiceMessage(result);
        }

        @Override
        public void onCancel() {
            mHandler.sendEmptyMessage(MessageConst.CLIENT_ACTION_CANCEL_RECORED);

        }

        @Override
        public void onUpdateVolume(int volume) {

        sendMessageToActivity(MessageConst.CLIENT_ACTION_UPDATA_VOLUME,volume,,null,null);

        }

    }

    private void initCommunicationAssist()
    {
        mVoiceSdkComAssist = new VoiceSdkComAssist();
        OlamiApplication.getInstance().setActivityToServiceListener(mVoiceSdkComAssist);
    }

    private void initXmly()
    {
        if(mBookUtil == null)
        {
            mBookUtil = BookUtil.getInstance();
            mBookUtil.init(VoiceSdkService.this);
            mBookUtil.setHandler(mHandler);
        }
    }

    private void processServiceMessage(String message)
    {
        String input = null;
        String serverMessage = null;
        try{
            JSONObject jsonObject = new JSONObject(message);
            JSONArray jArrayNli = jsonObject.optJSONObject("data").optJSONArray("nli");
            JSONObject jObj = jArrayNli.optJSONObject();
            JSONArray jArraySemantic = null;
            if(message.contains("semantic"))
              jArraySemantic = jObj.getJSONArray("semantic");
            else{
                input = jsonObject.optJSONObject("data").optJSONObject("asr").
                optString("result");
                sendMessageToActivity(MessageConst.
                                     CLIENT_ACTION_UPDATA_INPUT_TEXT, , , null, input);
                serverMessage = jObj.optJSONObject("desc_obj").opt("result").toString();
                sendMessageToActivity(MessageConst.
                        CLIENT_ACTION_UPDATA_SERVER_MESSAGE, , , null, serverMessage);
                return;
            }
            JSONObject jObjSemantic;
            JSONArray jArraySlots;
            JSONArray jArrayModifier;
            String type = null;
            String songName = null;
            String singer = null;


            if(jObj != null) {
                type = jObj.optString("type");
                if("musiccontrol".equals(type))
                {
                    jObjSemantic = jArraySemantic.optJSONObject();
                    input = jObjSemantic.optString("input");
                    jArraySlots = jObjSemantic.optJSONArray("slots");
                    jArrayModifier = jObjSemantic.optJSONArray("modifier");
                    String modifier = (String)jArrayModifier.opt();
                    if((jArrayModifier != null) && ("play".equals(modifier)))
                    {
                        if(jArraySlots != null)
                           for(int i=,k=jArraySlots.length(); i<k; i++)
                           {
                               JSONObject obj = jArraySlots.getJSONObject(i);
                               String name = obj.optString("name");
                               if("singer".equals(name))
                                   singer = obj.optString("value");
                               else if("songname".equals(name))
                                   songName = obj.optString("value");

                           }
                    }else if((modifier != null) && ("stop".equals(modifier)))
                    {
                        if(mBookUtil != null)
                            if(mBookUtil.isPlaying())
                                mBookUtil.stop();
                    }else if((modifier != null) && ("pause".equals(modifier)))
                    {
                        if(mBookUtil != null)
                            if(mBookUtil.isPlaying())
                                mBookUtil.pause();
                    }else if((modifier != null) && ("resume_play".equals(modifier)))
                    {
                        if(mBookUtil != null)
                            mBookUtil.resumePlay();
                    }else if((modifier != null) && ("add_volume".equals(modifier)))
                    {
                        if(mBookUtil != null)
                            mBookUtil.addVolume();
                    }else if((modifier != null) && ("del_volume".equals(modifier)))
                    {
                        if(mBookUtil != null)
                            mBookUtil.delVolume();
                    }else if((modifier != null) && ("next".equals(modifier)))
                    {
                        if(mBookUtil != null)
                            mBookUtil.next();
                    }else if((modifier != null) && ("previous".equals(modifier)))
                    {
                        if(mBookUtil != null)
                            mBookUtil.prev();
                    }else if((modifier != null) && ("play_index".equals(modifier)))
                    {
                        int position = ;
                        if(jArraySlots != null)
                               for(int i=,k=jArraySlots.length(); i<k; i++)
                               {
                                   JSONObject obj = jArraySlots.getJSONObject(i);
                                   JSONObject jNumDetial = obj.getJSONObject("num_detail");
                                   String index = jNumDetial.optString("recommend_value");
                                   position = Integer.parseInt(index) - ;
                               }
                        if(mBookUtil != null)
                            mBookUtil.skipTo(position);
                    }
                }
            }
            if(songName != null)
            {
                if(singer != null)
                {

                }else{
                    mBookUtil.searchBookAndPlay(songName,,);
                }
            }else if(singer != null)
            {
                mBookUtil.searchBookAndPlay(songName,,);
            }
            serverMessage = jObj.optJSONObject("desc_obj").opt("result").toString();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        //發送消息更新語音識别的文字
        sendMessageToActivity(MessageConst.CLIENT_ACTION_UPDATA_INPUT_TEXT, , , null, input);
        //發送消息更新伺服器傳回的結果字元串
        sendMessageToActivity(MessageConst.CLIENT_ACTION_UPDATA_SERVER_MESSAGE, 
                                                    , , null, serverMessage);

    }

    private void sendMessageToActivity(int what, int arg1, int arg2, Bundle data, Object obj)
    {
        if(OlamiApplication.getInstance().getServiceToActivityListener() != null)
            OlamiApplication.getInstance().getServiceToActivityListener().
                                             callBack(what, arg1, arg2, data, obj);
    }

    private class VoiceSdkComAssist implements CommunicationAssist{

        @Override
        public void callBack(int what, int arg1, int arg2, Bundle data,Object obj) {
            Message msg = Message.obtain(null, what);
            msg.arg1 = arg1;
            msg.arg2 = arg2;
            if (data != null)
                msg.setData(data);
            if (obj != null)
                msg.obj = obj;
            mInComingHandler.sendMessage(msg);
        }       
    }

    @Override
    public void onDestroy(){
        super.onDestroy();
        if(mOlamiVoiceRecognizer != null)
            mOlamiVoiceRecognizer.destroy();
        if(mBookUtil != null)
        {
            mBookUtil.destroy();
        }

    }


}
           

3.4 VoiceSdkService中onResult的回調處理

在VoiceSdkService.java中processServiceMessage(String message)用于處理onResult的回調資料。例如“我要聽三國演義”傳回如下資料:
           
{
    "data": {
        "asr": {
            "result": "我要聽三國演義",
            "speech_status": ,
            "final": true,
            "status": 
        },
        "nli": [
            {
                "desc_obj": {
                    "result": "正在努力搜尋中,請稍等",
                    "status": 
                },
                "semantic": [
                    {
                        "app": "musiccontrol",
                        "input": "我要聽三國演義",
                        "slots": [
                            {
                                "name": "songname",
                                "value": "三國演義" }
                        ],
                        "modifier": [
                            "play"
                        ],
                        "customer": "58df512384ae11f0bb7b487e"
                    }
                ],
                "type": "musiccontrol"
            }
        ]
    },
    "status": "ok"
}
           

1)解析出nli中type類型是musiccontrol,這是文法傳回app的類型,而這個線上聽書的demo隻關心musiccontrol這 個app類型,其他的忽略。

2)使用者說的話轉成文字是在asr中的result中擷取

3)在nli中的semantic中,input值是使用者說的話,同asr中的result。

modifier代表傳回的行為動作,此處可以看到是play就是要求播放,slots中的資料表示歌曲名稱是三國演義。

那麼動作是play,内容是歌曲名稱是三國演義,在這個demo中調用

mBookUtil.searchBookAndPlay(songName,0,0);會先查詢,查詢到結果會再發播放消息要求播放,我要聽三國演義這個流程就走完了。

4.BookUtil

說一下搜尋聽書的實作過程
public void searchBookInfo(String bookName,final int index,final boolean isNeedPlay)
{
    mBookName = bookName;
    Map<String, String> param = new HashMap<String, String>();
    param.put(DTransferConstants.SEARCH_KEY, bookName);
    param.put(DTransferConstants.CATEGORY_ID, "" + );//此處3代表搜尋的是聽書
    //param.put(DTransferConstants.PAGE, "" + mPageId);
    param.put(DTransferConstants.SORT, "asc");//傳回清單的排序是正序還是逆序
    param.put(DTransferConstants.PAGE_SIZE, "" + PAGE_SIZE);//每頁能傳回多少個查詢結果
    mPage = (index/PAGE_SIZE)+;//目前在第幾頁

    mPlayerManager = XmPlayerManager.getInstance(mContext);//喜馬拉雅初始化部分
    mPlayerManager.init(mNotificationId, null);
    mPlayerManager.addPlayerStatusListener(mPlayerStatusListener);
    mPlayerManager.addAdsStatusListener(mAdsListener);

    CommonRequest.getSearchedAlbums(param, new IDataCallBack<SearchAlbumList>()
    {

        @Override
        public void onSuccess(SearchAlbumList object)   
        {                   
            if (object != null && object.getAlbums() != null
                    && object.getAlbums().size() != )
            {
                if (mSearchAlbumList == null)
                {
                    mSearchAlbumList = object;
                }
                else
                {
                    mSearchAlbumList.getAlbums().addAll(object.getAlbums());
                }
                //mTrackAdapter.notifyDataSetChanged();

                Map<String, String> map = new HashMap<String, String>();

                map.put(
                DTransferConstants.ALBUM_ID, ""+object.getAlbums().get().getId());
                map.put(DTransferConstants.SORT, "asc");
                map.put(DTransferConstants.PAGE, "" + mPage);
                map.put(DTransferConstants.PAGE_SIZE,  "" + PAGE_SIZE);

                CommonRequest.getTracks(map, new IDataCallBack<TrackList>()
                {

                        @Override
                        public void onSuccess(TrackList object)
                        {
                            mTrackList = object;
                            mTotalCount = mTrackList.getTotalCount();
                            if(mTrackList.getTracks().size() <= )
                                return;
                            String str = "專輯:"+mTrackList.getAlbumTitle()+
                                            get().getTrackTitle().toString();
                            if(isNeedPlay)
                            {
                                mPosition = index % PAGE_SIZE;
                                mHandler.sendMessage(mHandler.obtainMessage(
                                MessageConst.CLIENT_ACTION_PLAY_BOOK_AFTER_SEARCH, 
                                index % PAGE_SIZE,));//此處mTrackList中已經查詢出結果
                                //向VoiceSdkService發送消息進行播放
                            }
                            else
                                sendBookInfoToServer();                                 
                        }
                        @Override
                        public void onError(int code, String message)
                        {
                            Log.i("ppp","error: "+message);
                            sendBookInfoToServer();
                        }
                });

            }
        }

        @Override
        public void onError(int code, String message)
        {
              Log.i("ppp","error: "+message);
              sendBookInfoToServer();
        }
    });
}
           

5.demo中支援的說法

我想聽西遊記

我要聽西遊記

播放西遊記

聽西遊記

我想聽西遊記這本書

上一首

上一回

下一首

下一回

暫停/暫停播放

繼續/繼續播放

聲音大一點

聲音小一點

關閉/關閉播放

用的是喜馬拉雅測試賬号,隻支援聽書的功能,查找歌曲的結果傳回為空。

6.源碼下載下傳連結

用olamisdk語音識别引擎做線上聽書demo

7.相關連結

語音記賬demo:http://blog.csdn.net/ls0609/article/details/72765789

olami開放平台文法編寫簡介:http://blog.csdn.net/ls0609/article/details/71624340

olami開放平台文法官方介紹:https://cn.olami.ai/wiki/?mp=nli&content=nli2.html

繼續閱讀