android 融云 + 科大讯飞 实现仿微信语音消息转换为文字
2017-04-20 10:30
2336 查看
融云SDK 使用很方便,简单配置就可以搭建即时通讯功能,配合科大讯飞的语音识别, 即可实现微信中语音消息转换为文字的功能
融云sdk的基本使用就不细说了, 网上很多资料
使用融云sdk自带的聊天会话界面,想要在此会话界面上增加语音消息长按时弹出 “转换为文字” 的菜单, 只需实现聊天会话界面的事件监听即可,监听类为:
初始化融云之后,设置自定义的会话界面事件监听器:
融云sdk的基本使用就不细说了, 网上很多资料
使用融云sdk自带的聊天会话界面,想要在此会话界面上增加语音消息长按时弹出 “转换为文字” 的菜单, 只需实现聊天会话界面的事件监听即可,监听类为:
ConversationBehaviorListener
融云默认的消息点击事件由MessageListAdapter 类设置,即在消息列表的适配器中定义点击,长按等事件处理器。 其中的消息长按事件处理代码为: MessageListAdapter类的 bindView()方法
view.setOnLongClickListener(new OnLongClickListener() { public boolean onLongClick(View v) { if(RongContext.getInstance().getConversationBehaviorListener() != null && RongContext.getInstance().getConversationBehaviorListener().onMessageLongClick(MessageListAdapter.this.mContext, v, data)) { return false; } else { MessageProvider provider = RongContext.getInstance().getMessageTemplate(data.getContent().getClass()); provider.onItemLongClick(v, position, data.getContent(), data); return false; } } });
可以发现,当ConversationBehaviorListener的onMessageLongClick方法返回true时 ,不再执行融云默认的消息长按处理, 返回fasle即由融云默认的MessageProvider来负责处理。 因此我们只需要实现自定义的ConversationBehaviorListener ,重写其中的 onMessageLongClick方法,并返回true,即可实现自定义的消息长按事件 ,我们这里的需求是 实现语音消息长按,弹出 “转换为文字” 的菜单。
初始化融云之后,设置自定义的会话界面事件监听器:
/*init rongcloud*/ RongIM.init(this); RongIM.setConversationBehaviorListener(new RongIM.ConversationBehaviorListener() { @Override public boolean onUserPortraitClick(Context context, Conversation.ConversationType conversationType, UserInfo userInfo) { return false; } @Override public boolean onUserPortraitLongClick(Context context, Conversation.ConversationType conversationType, UserInfo userInfo) { return false; } @Override public boolean onMessageClick(Context context, View view, Message message) { return false; } @Override public boolean onMessageLongClick(Context context, View view, Message message) { //会话界面消息长按回调方法 ,如果是语音消息则使用自定义的 MyVoiceMessageItemProvider ,否则使用融云默认处理器 if(message.getContent() instanceof VoiceMessage){ IContainerItemProvider.MessageProvider provider = new MyVoiceMessageItemProvider(context); provider.onItemLongClick(view, 0, message.getContent(), message); return true; }else{ return false; //返回false ,使用融云默认处理 } } });
重写了 onMessageLongClick 方法。 由于只需要增加对语音消息的处理,所以先对消息类型判断,如果是语音消息VoiceMessage则 自定义处理器MyVoiceMessageItemProvider ,该类的实现后面会讲。其他消息(文字,图片)由融云默认处理。 设置了语音消息长按的事件监听器后,接下来实现 “转化为文字” 菜单的弹出 。 融云默认的语音消息长按弹出菜单由 RongIM类初始化时注册的 VoiceMessageItemProvider 类实现,如下:RongIM 类中的init()方法:registerMessageTemplate(new TextMessageItemProvider()); registerMessageTemplate(new ImageMessageItemProvider()); registerMessageTemplate(new LocationMessageItemProvider()); registerMessageTemplate(new VoiceMessageItemProvider(context)); registerMessageTemplate(new DiscussionNotificationMessageItemProvider()); registerMessageTemplate(new InfoNotificationMsgItemProvider()); registerMessageTemplate(new RichContentMessageItemProvider()); registerMessageTemplate(new PublicServiceMultiRichContentMessageProvider()); registerMessageTemplate(new PublicServiceRichContentMessageProvider()); registerMessageTemplate(new HandshakeMessageItemProvider()); registerMessageTemplate(new UnknownMessageItemProvider());VoiceMessageItemProvider 类中处理消息长按的代码:public void onItemLongClick(View view, int position, VoiceMessage content, final Message message) { String name = null; if(!message.getConversationType().getName().equals(ConversationType.APP_PUBLIC_SERVICE.getName()) && !message.getConversationType().getName().equals(ConversationType.PUBLIC_SERVICE.getName())) { UserInfo items1 = (UserInfo)RongContext.getInstance().getUserInfoCache().get(message.getSenderUserId()); if(items1 != null) { name = items1.getName(); } } else { ConversationKey items = ConversationKey.obtain(message.getTargetId(), message.getConversationType()); PublicServiceInfo info = (PublicServiceInfo)RongContext.getInstance().getPublicServiceInfoCache().get(items.getKey()); if(info != null) { name = info.getName(); } } String[] items2 = new String[]{view.getContext().getResources().getString(string.rc_dialog_item_message_delete)}; ArraysDialogFragment.newInstance(name, items2).setArraysDialogItemListener(new OnArraysDialogItemListener() { public void OnArraysDialogItemClick(DialogInterface dialog, int which) { if(which == 0) { RongIM.getInstance().getRongIMClient().deleteMessages(new int[]{message.getMessageId()}, (ResultCallback)null); } } }).show(((FragmentActivity)view.getContext()).getSupportFragmentManager()); }
只实现了 长按时弹出“删除”按钮 ,并删除此消息的功能。
我们只需要继承此类,并重写此onItemLongClick方法,在其中增加一个 “转化为文字”的弹出按钮,在配合科大讯飞的语音识别功能,即可实现微信那样的语音消息转文字功能。
自定义的MyVoiceMessageItemProvider类,继承自VoiceMessageItemProvider/** * 会话界面事件处理类 */ public class MyVoiceMessageItemProvider extends VoiceMessageItemProvider { private Context context; //转换后显示文字 private TextView textView; public MyVoiceMessageItemProvider(Context context) { super(context); this.context = context; } //语音消息长按处理回调方法 @Override public void onItemLongClick(View view, int position, final VoiceMessage content, final Message message) { String name = null; if(!message.getConversationType().getName().equals(Conversation.ConversationType.APP_PUBLIC_SERVICE.getName()) && !message.getConversationType().getName().equals(Conversation.ConversationType.PUBLIC_SERVICE.getName())) { UserInfo items1 = (UserInfo)RongContext.getInstance().getUserInfoCache().get(message.getSenderUserId()); if(items1 != null) { name = items1.getName(); } } else { ConversationKey items = ConversationKey.obtain(message.getTargetId(), message.getConversationType()); PublicServiceInfo info = (PublicServiceInfo)RongContext.getInstance().getPublicServiceInfoCache().get(items.getKey()); if(info != null) { name = info.getName(); } } String[] items2 = new String[]{view.getContext().getResources().getString(io.rong.imkit.R.string.rc_dialog_item_message_delete), view.getContext().getResources().getString(io.rong.imkit.R.string.rc_dialog_item_message_convert)}; ArraysDialogFragment.newInstance(name, items2).setArraysDialogItemListener(new ArraysDialogFragment.OnArraysDialogItemListener() { public void OnArraysDialogItemClick(DialogInterface dialog, int which) { if(which == 0) { RongIM.getInstance().getRongIMClient().deleteMessages(new int[]{message.getMessageId()}, (RongIMClient.ResultCallback)null); } else if(which == 1){ //初始化 语音转化为文字 界面 LayoutInflater factory = LayoutInflater.from(context); RelativeLayout view = (RelativeLayout)factory.inflate(R.layout.convert_dialog, null); final AlertDialog dlg = new AlertDialog.Builder(context).create(); textView = new TextView(context); RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); params.addRule(RelativeLayout.CENTER_IN_PARENT); textView.setLayoutParams(params); textView.setTextColor(Color.BLACK); textView.setTextSize(20f); textView.setText("正在转换..."); view.addView(textView); if ( !dlg.isShowing()) { dlg.show(); } dlg.setContentView(view); Activity activity = (Activity) context; String voicePath = FileUtils.uri2File(activity,content.getUri()); //调用科大讯飞处理类解析语音文件 new IflytekHandle(voicePath,context){ @Override public void returnWords(String words){ textView.setText(words); } }; } } }).show(((FragmentActivity)view.getContext()).getSupportFragmentManager()); } }
该类在语音消息长按时 ,弹出“删除” ,“转换为文字”两个按钮 ,点击 “转换为文字” ,弹出一个Dialog ,并将语音消息中的音频文件URI地址转为绝对路径后,交由科大讯飞识别,识别成功后显示在Dialog 中。负责处理语音识别的类为 IflytekHandle ,
下面会贴出代码。
由于科大讯飞只能识别 pcm和wav格式的音频流文件,而融云的语音消息文件格式为 AMR,因此识别前需将本地的AMR录音文件解码为pcm。解码类 AudioDecode 来自于网上开源/** * 科大讯飞语音识别工具 */ public abstract class IflytekHandle { // 用HashMap存储听写结果 private HashMap<String,String> mIatResults = new LinkedHashMap<>(); private static SpeechRecognizer mIat; // 引擎类型 private String mEngineType = SpeechConstant.TYPE_CLOUD; //解码转换 private AudioDecode audioDecode; public IflytekHandle(String filePath , Context context){ voice2words(filePath,context); } public void voice2words (String filePath , Context context){ mIatResults.clear(); if(mIat == null){ //1、创建SpeechRecognizer对象,第二个参数:本地识别时传InitListener mIat = SpeechRecognizer.createRecognizer(context,null); setParam(); mIat.setParameter(SpeechConstant.AUDIO_SOURCE, "-1"); mIat.setParameter(SpeechConstant.SAMPLE_RATE, "8000");//设置正确的采样率 } int ret = 0; // 函数调用返回值 ret = mIat.startListening(mRecognizerListener); if (ret != ErrorCode.SUCCESS) { } else { //iatFun();//讯飞demo里面的方法 audioDecodeFun(filePath); } } //听写监听器 private RecognizerListener mRecognizerListener = new RecognizerListener() { //volume音量值0~30,data音频数据 @Override public void onVolumeChanged(int volume, byte[] bytes) { } //开始录音 // 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入 @Override public void onBeginOfSpeech() { } //结束录音 @Override public void onEndOfSpeech() { } /** * 听写结果回调接口,返回Json格式结果 * 一般情况下会通过onResults接口多次返回结果,完整的识别内容是多次结果的累加 * isLast等于true时会话结束。 */ @Override public void onResult(RecognizerResult recognizerResult, boolean b) { printResult(recognizerResult); } //会话发生错误回调接口 // Tips: // 错误码:10118(您没有说话),可能是录音机权限被禁,需要提示用户打开应用的录音权限。 @Override public void onError(SpeechError speechError) { returnWords(speechError.getErrorDescription()); } //扩展用接口 @Override public void onEvent(int eventType, int arg1, int arg2, Bundle bundle) { } }; private void printResult(RecognizerResult recognizerResult) { String text = JsonParser.parseIatResult(recognizerResult.getResultString()); String sn = null; //读取Json结果中的sn字段 try { JSONObject resultJson = new JSONObject(recognizerResult.getResultString()); sn = resultJson.optString("sn"); }catch (Exception e){ e.printStackTrace(); } mIatResults.put(sn,text); StringBuilder sb = new StringBuilder(); for (String key:mIatResults.keySet()){ sb.append(mIatResults.get(key)); } returnWords(sb.toString()); } //回调方法 ,返回识别后的文字 public abstract void returnWords(String words); /** * 工具类 * @param audioPath */ private void audioDecodeFun(String audioPath){ audioDecode = AudioDecode.newInstance(); audioDecode.setFilePath(audioPath); audioDecode.prepare(); audioDecode.setOnCompleteListener(new AudioDecode.OnCompleteListener() { @Override public void completed(final ArrayList<byte[]> pcmData) { if(pcmData!=null){ //写入音频文件数据,数据格式必须是采样率为8KHz或16KHz(本地识别只支持16K采样率,云端都支持),位长16bit,单声道的wav或者pcm //必须要先保存到本地,才能被讯飞识别 //为防止数据较长,多次写入,把一次写入的音频,限制到 64K 以下,然后循环的调用wirteAudio,直到把音频写完为止 for (byte[] data : pcmData){ mIat.writeAudio(data, 0, data.length); } Log.d("-----------stop",System.currentTimeMillis()+""); mIat.stopListening(); }else{ mIat.cancel(); } audioDecode.release(); } }); audioDecode.startAsync(); } /** * 参数设置 */ private void setParam(){ //参数设置 /** * 应用领域 服务器为不同的应用领域,定制了不同的听写匹配引擎,使用对应的领域能获取更 高的匹配率 * 应用领域用于听写和语音语义服务。当前支持的应用领域有: * 短信和日常用语:iat (默认) * 视频:video * 地图:poi * 音乐:music */ mIat.setParameter(SpeechConstant.DOMAIN,"iat"); /** * 在听写和语音语义理解时,可通过设置此参数,选择要使用的语言区域 * 当前支持: * 简体中文:zh_cn(默认) * 美式英文:en_us */ mIat.setParameter(SpeechConstant.LANGUAGE,"zh_cn"); /** * 每一种语言区域,一般还有不同的方言,通过此参数,在听写和语音语义理解时, 设置不同的方言参数。 * 当前仅在LANGUAGE为简体中文时,支持方言选择,其他语言区域时, 请把此参数值设为null。 * 普通话:mandarin(默认) * 粤 语:cantonese * 四川话:lmz * 河南话:henanese */ mIat.setParameter(SpeechConstant.ACCENT,"mandarin"); // 设置听写引擎 mIat.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType); //设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理 //默认值:短信转写5000,其他4000 mIat.setParameter(SpeechConstant.VAD_BOS,"4000"); // 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入, 自动停止录音 mIat.setParameter(SpeechConstant.VAD_EOS,"1000"); // 设置标点符号,设置为"0"返回结果无标点,设置为"1"返回结果有标点 mIat.setParameter(SpeechConstant.ASR_PTT,"1"); // 设置音频保存路径,保存音频格式支持pcm、wav mIat.setParameter(SpeechConstant.AUDIO_FORMAT,"wav"); //mIat.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory()+"/msc/iat.wav"); //文本,编码 mIat.setParameter(SpeechConstant.TEXT_ENCODING, "utf-8"); } }
效果
相关文章推荐
- android 语音转换文字(科大讯飞SDK简易封装)
- android自学之TextToSpeech实现文字向语音的转换
- 【从零单排】利用科大讯飞语音包实现Android语音识别Demo
- FreeTTS实现语音合成-----将IRC消息转换成听得见的语音
- 使用科大讯飞语音SDK实现文字在线合成语音
- Android QQ、微信聊天消息界面设计原理与实现
- android 轻松实现在线即时聊天【图片、语音、表情、文字】等!含源码!
- 仿微信、短信、QQ等消息数目右上角红色小圆球气泡显示(基于Android XML布局文件实现)
- 科大讯飞语音实现Android拨号之一
- Android QQ、微信聊天消息界面设计原理与实现
- Android Fragment + ViewPager 实现类微信 底部导航栏 和 显示消息提醒
- 将企业协作与微信结合,明道打通微信实现从微信到明道的消息分享,目前已支持文字、图片以及链接
- android 轻松实现在线即时聊天【图片、语音、表情、文字】等!含源码!
- 科大讯飞语音实现Android拨号之二
- android实现微信语音功能
- Android实现微信底部的带消息提示数量
- Android实现微信底部的带消息提示数量
- android 轻松实现在线即时聊天【图片、语音、表情、文字】等!含源码!
- iOS 使用科大讯飞技术实现语音转文字(语音听写)