package io.rong.recognizer; import android.content.Context; import android.graphics.drawable.AnimationDrawable; import android.os.Bundle; import android.os.Environment; import android.text.TextUtils; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import com.iflytek.cloud.ErrorCode; import com.iflytek.cloud.InitListener; import com.iflytek.cloud.RecognizerListener; import com.iflytek.cloud.RecognizerResult; import com.iflytek.cloud.SpeechConstant; import com.iflytek.cloud.SpeechError; import com.iflytek.cloud.SpeechRecognizer; import com.iflytek.cloud.SpeechUtility; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONTokener; import java.util.Locale; import java.util.Random; import io.rong.common.RLog; public class Recognizer extends RelativeLayout implements RecognizerListener { private final static String TAG = "Recognizer"; private ImageView imgMic; private RelativeLayout rlBottom; private Random random; private IRecognizedResult resultCallBack; private SpeechRecognizer mIat = null; private AnimationDrawable animStart; private AnimationDrawable animEnd; private static String mAppId; /** * 开发者可以通过此接口设置自己从科大讯飞官网申请的 appId。 * 此方法可以在 SDK init 之后调用。 * * @param appId 自定义的 appId */ public static void setAppId(String appId) { mAppId = appId; } public Recognizer(Context context) { super(context); /** * 语音输入模块集成的是讯分SDK,开发者要集成需要去讯飞开放平台获取自身的APPID替换下面的id. */ if (SpeechUtility.getUtility() == null) { SpeechUtility.createUtility(context.getApplicationContext(), SpeechConstant.APPID + "=" + (mAppId == null ? "581f2927" : mAppId)); } initViews(); } public Recognizer(Context context, AttributeSet attrs) { super(context, attrs); initViews(); } private void initViews() { setClickable(true); setBackgroundColor(getResources().getColor(R.color.rc_recognizerview_bg_normal)); RelativeLayout recognizerContainer = (RelativeLayout) LayoutInflater.from(getContext()).inflate(R.layout.rc_view_recognizer, null); View rlMic = recognizerContainer.findViewById(R.id.rl_mic); rlMic.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { if (mIat == null || !mIat.isListening()) { startRecognize(); } else { reset(); } } }); imgMic = (ImageView) recognizerContainer.findViewById(R.id.img_mic); TextView tvClear = (TextView) recognizerContainer.findViewById(R.id.btn_clear); tvClear.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (null != resultCallBack) { resultCallBack.onClearClick(); } } }); rlBottom = (RelativeLayout) recognizerContainer.findViewById(R.id.rl_bottom); addView(recognizerContainer); random = new Random(); } /** * 初始化监听器。 */ private static InitListener mInitListener = new InitListener() { @Override public void onInit(int code) { RLog.i(TAG, "onInit " + code); } }; public void startRecognize() { if (null == mIat) { mIat = SpeechRecognizer.createRecognizer(getContext(), mInitListener); } if (mIat.isListening()) { return; } setParam(); int ret = mIat.startListening(this); if (ret != ErrorCode.SUCCESS) { RLog.d(TAG, "startRecognize ret error " + ret); } } /** * 参数设置,设置听写参数,详见《科大讯飞MSC API手册(Android)》SpeechConstant类 * * @param * @return */ private void setParam() { // 清空参数 mIat.setParameter(SpeechConstant.PARAMS, null); // 设置听写引擎 mIat.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD); // 设置返回结果格式 mIat.setParameter(SpeechConstant.RESULT_TYPE, "json"); mIat.setParameter(SpeechConstant.DOMAIN, "iat"); if ("zh".equals(Locale.getDefault().getLanguage().toLowerCase())) { mIat.setParameter(SpeechConstant.LANGUAGE, "zh_cn"); mIat.setParameter(SpeechConstant.ACCENT, "mandarin "); } else { mIat.setParameter(SpeechConstant.LANGUAGE, "en_us"); } // 设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理 mIat.setParameter(SpeechConstant.VAD_BOS, "4000"); // 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入, 自动停止录音 mIat.setParameter(SpeechConstant.VAD_EOS, "1000"); // 设置标点符号,设置为"0"返回结果无标点,设置为"1"返回结果有标点 mIat.setParameter(SpeechConstant.ASR_PTT, "1"); // 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限 // 注:AUDIO_FORMAT参数语记需要更新版本才能生效 mIat.setParameter(SpeechConstant.AUDIO_FORMAT, "wav"); mIat.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory() + "/msc/iat.wav"); } private void setRandomImageResource() { int num = random.nextInt(3) + 1; switch (num) { case 1: imgMic.setImageResource(R.drawable.rc_recognize_volume_01); break; case 2: imgMic.setImageResource(R.drawable.rc_recognize_volume_02); break; default: imgMic.setImageResource(R.drawable.rc_recognize_volume_03); break; } } private void changeVolume(int volume) { if (null != imgMic) { switch (volume / 2) { case 0: setRandomImageResource(); break; case 1: imgMic.setImageResource(R.drawable.rc_recognize_volume_02); break; case 2: imgMic.setImageResource(R.drawable.rc_recognize_volume_03); break; case 3: imgMic.setImageResource(R.drawable.rc_recognize_volume_04); break; case 4: imgMic.setImageResource(R.drawable.rc_recognize_volume_05); break; case 5: imgMic.setImageResource(R.drawable.rc_recognize_volume_06); break; case 6: imgMic.setImageResource(R.drawable.rc_recognize_volume_07); break; case 7: imgMic.setImageResource(R.drawable.rc_recognize_volume_08); break; case 8: imgMic.setImageResource(R.drawable.rc_recognize_volume_09); break; case 9: imgMic.setImageResource(R.drawable.rc_recognize_volume_10); break; case 10: imgMic.setImageResource(R.drawable.rc_recognize_volume_11); break; case 11: imgMic.setImageResource(R.drawable.rc_recognize_volume_12); break; case 12: imgMic.setImageResource(R.drawable.rc_recognize_volume_13); break; default: imgMic.setImageResource(R.drawable.rc_recognize_volume_14); break; } } } private void endOfSpeech() { if (null == imgMic) return; imgMic.setImageResource(R.drawable.rc_anim_speech_end); animEnd = (AnimationDrawable) imgMic.getDrawable(); imgMic.clearAnimation(); animEnd.start(); } private void beginOfSpeech() { if (null == imgMic) return; imgMic.setImageResource(R.drawable.rc_anim_speech_start); animStart = (AnimationDrawable) imgMic.getDrawable(); imgMic.clearAnimation(); animStart.start(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (null != mIat) { mIat.cancel(); mIat.destroy(); mIat = null; } if (null != resultCallBack) { resultCallBack = null; } if (animEnd != null) { animEnd.stop(); animEnd = null; } if (animStart != null) { animStart.stop(); animEnd = null; } if (SpeechUtility.getUtility() != null) { SpeechUtility.getUtility().destroy(); } mInitListener = null; } private String parseIatResult(String json) { { StringBuffer ret = new StringBuffer(); JSONTokener jsonTokener = new JSONTokener(json); try { JSONObject jsonObject = new JSONObject(jsonTokener); JSONArray jsonArray = jsonObject.getJSONArray("ws"); for (int i = 0; i < jsonArray.length(); ++i) { // 转写结果词,默认使用第一个结果 JSONArray items = jsonArray.getJSONObject(i).getJSONArray("cw"); JSONObject obj = items.getJSONObject(0); // 如果需要多候选结果,解析数组其他字段0 // for(int j = 0; j < items.length(); j++) // { // JSONObject obj = items.getJSONObject(j); // ret.append(obj.getString("w")); // } ret.append(obj.getString("w")); } } catch (JSONException e) { e.printStackTrace(); } return ret.toString(); } } private void printResult(RecognizerResult result) { String json = result.getResultString(); String text = parseIatResult(json); try { JSONObject obj = new JSONObject(json); boolean isLast = obj.getBoolean("ls"); if (isLast) { endOfSpeech(); } } catch (JSONException e) { e.printStackTrace(); } if (!TextUtils.isEmpty(text)) { rlBottom.setVisibility(View.VISIBLE); } if (resultCallBack != null) { resultCallBack.onResult(text); } } @Override public void onResult(RecognizerResult recognizerResult, boolean b) { printResult(recognizerResult); if (imgMic != null) { imgMic.setImageResource(R.drawable.rc_recognize_disable); } } @Override public void onError(SpeechError speechError) { if (speechError.getErrorCode() == ErrorCode.ERROR_NO_NETWORK) { Toast.makeText(getContext(), getContext().getString(R.string.rc_plugin_recognize_check_network), Toast.LENGTH_SHORT).show(); } if (imgMic != null) { imgMic.setImageResource(R.drawable.rc_recognize_disable); } } @Override public void onEvent(int eventType, int i1, int i2, Bundle bundle) { RLog.d(TAG, "RecognizerView onEvent eventType: " + eventType); } @Override public void onVolumeChanged(int volume, byte[] bytes) { changeVolume(volume); } @Override public void onBeginOfSpeech() { RLog.d(TAG, "RecognizerView onBeginOfSpeech"); beginOfSpeech(); } @Override public void onEndOfSpeech() { RLog.d(TAG, "RecognizerView onEndOfSpeech"); endOfSpeech(); } public void setResultCallBack(IRecognizedResult resultCallBack) { this.resultCallBack = resultCallBack; } private void reset() { if (null != mIat) { mIat.cancel(); mIat.destroy(); mIat = null; } if (null != animEnd) { animEnd.stop(); animEnd = null; } if (animStart != null) { animStart.stop(); animStart = null; } if (imgMic != null) { imgMic.setImageResource(R.drawable.rc_recognize_disable); } } }