数据结构

  1. public LongSparseArray<RecordBean> recordList=new LongSparseArray<>();
封装所有相关数据的Bean

  1. public class RecordBean {
  2. public static final String VOICE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "voice";
  3. public static final String XF_VOICE_FILE_PATH = VOICE_PATH + File.separator + "xf_temp_file.wav";
  4. public String pcmPath;
  5. public String aacPath;
  6. public boolean startRecordSuccess = false;//开始录音成功,即调用JJMediaSDK.startRecord的返回值
  7. public boolean endRecordSuccess = false;//结束录音成功,即调用JJMediaSDK.stopRecord的返回值
  8. public long startTime;//开始录音时间
  9. public long endTime;//结束录音时间
  10. public boolean sendVoiceSuccess = false;//上传并发送成功
  11. public boolean translateSuccess = false;//翻译成功
  12. public String qnKey;//上传到七牛的语音地址
  13. public long clientid;//当上传七牛成功后发送一条socket语音消息,此消息需要客户端生成一个clientid。同时这也是集合recordList中的key
  14. public int msgid;//当上传七牛成功后发送一条socket语音消息,服务器响应时会返回一个msgid,以后就需根据msgid发送及获取语音对应的文本
  15. public String content;//讯飞翻译出的内容
  16. public RecordBean(long time) {
  17. clientid = time;
  18. String data = new SimpleDateFormat("yyyy_MM_dd HH_mm_ss SSS", Locale.getDefault()).format(new Date(time));
  19. this.aacPath = VOICE_PATH + File.separator + "aac_" + data + ".aac";
  20. this.pcmPath = VOICE_PATH + File.separator + "pcm_" + data + ".pcm";
  21. L.i("语音文件保存路径:" + pcmPath + "\n" + aacPath);
  22. }
  23. public static class VoiceBean {//发送的语音消息的内容。仅仅包含语音URL路径以及语音时长
  24. String path;
  25. int duration;
  26. public VoiceBean(String path, int duration) {
  27. this.path = path;
  28. this.duration = duration;
  29. }
  30. }
  31. }

长按录制语音相关逻辑

  1. //长按说话
  2. mChatInputWidget.setPressListener(new PressListener() {
  3. private long key;//集合recordList中的key,同时也是RecordBean中的clientid,同时也是语音消息的clientid
  4. @Override
  5. public void pressing() {
  6. if (micOnoff == 1) {//是否正在上麦(占用录音设备):0,未指定;1,开;2,关
  7. ToastHelper.showToastInThread("亲,发送语音功能需要下麦哦...");
  8. } else { //注意,以下代码要在具有录音权限时访问
  9. AudioManager audioManager = (AudioManager) ChatActivity.this.getSystemService(Context.AUDIO_SERVICE);
  10. audioManager.setMicrophoneMute(false);//关闭麦克风静音
  11. //键为当前时间,以保证唯一性
  12. key = System.currentTimeMillis();
  13. RecordBean recordBean = new RecordBean(key);
  14. recordList.put(key, recordBean);
  15. //调用JNI代码启动录音,录音成功后会生成两个文件,这两个文件的存放位置封装在了recordBean中
  16. JJMediaSDK.captureStop();//停止录音
  17. recordBean.startRecordSuccess = JJMediaSDK.startRecord(recordBean.pcmPath, recordBean.aacPath);
  18. recordBean.startTime = System.currentTimeMillis();//记录开始和结束录音时间,以获取录音时长
  19. if (!recordBean.startRecordSuccess) Toast.makeText(ChatActivity.this, "录音失败", Toast.LENGTH_SHORT).show();
  20. else pressSpeeking("正在说话...");//一个自定义的UI,当按住的时候一直显示,当松开的时候消失
  21. }
  22. }
  23. @Override
  24. public void pressStop() {
  25. RecordBean recordBean = recordList.get(key);//获取当前key对应的RecordBean
  26. if (recordBean == null || !recordBean.startRecordSuccess) {//肯定是录音失败了
  27. Toast.makeText(ChatActivity.this, "录音失败", Toast.LENGTH_SHORT).show();
  28. return;
  29. }
  30. AudioManager audioManager = (AudioManager) ChatActivity.this.getSystemService(Context.AUDIO_SERVICE);
  31. audioManager.setMicrophoneMute(true);//开启麦克风静音
  32. //调用JNI代码结束录音。JNI同事说这里不会失败的。谁信啊,万一存储空间不足或手机突然爆掉了呢?
  33. recordBean.endRecordSuccess = JJMediaSDK.stopRecord();
  34. recordBean.endTime = System.currentTimeMillis();
  35. pressSpeekingdimiss();
  36. if (!recordBean.endRecordSuccess) {
  37. Toast.makeText(ChatActivity.this, "录音失败", Toast.LENGTH_SHORT).show();
  38. return;
  39. }
  40. //****************************************【第一个异步:异步上传语音】*****************************************
  41. //异步上传语音到七牛,当上传成功后发送一条socket消息通知语音的URL路径,此时其他用户就可以播放此语音了
  42. //同时,服务器收到此消息后会做出响应,主要是返回一个msgid,以后就可以根据msgid通知及获取语音对应的文本了
  43. VoiceUtils.uploadVoiceAndSendMsg(ChatActivity.this, recordBean);
  44. //****************************************【第二个异步:异步讯飞翻译】*****************************************
  45. if (mIat == null) mIat = VoiceUtils.getSpeechRecognizerInstance(ChatActivity.this);//讯飞SDK初始化
  46. if (mIat == null) return;//讯飞SDK初始化失败
  47. //讯飞SDK翻译过程及状态监听
  48. RecognizerListener recognizerListener = VoiceUtils.getRecognizerListener(content -> {//翻译成功回调
  49. recordBean.content = content;
  50. recordBean.translateSuccess = true;
  51. Toast.makeText(ChatActivity.this, recordBean.content, Toast.LENGTH_SHORT).show();
  52. //**************************【将上传到七牛的语音和讯飞翻译的内容关联起来】******************************
  53. //如果第一个异步已经完成,则直接发一条socket消息,将两者内容联系在一起;否则等第一个异步完成以后再做处理
  54. if (recordBean.sendVoiceSuccess) SendMsgUtils.sendVoiceContentMsg(ChatActivity.this, recordBean);
  55. });
  56. // 函数调用返回值
  57. int ret = mIat.startListening(recognizerListener);
  58. if (ret != ErrorCode.SUCCESS) {
  59. Toast.makeText(ChatActivity.this, "翻译失败", Toast.LENGTH_SHORT).show();
  60. L.i("讯飞SDK翻译失败,错误码:" + ret);
  61. } else {
  62. byte[] audioData = VoiceUtils.readSDFile(recordBean.pcmPath);//读取SDK返回的pcm文件流
  63. if (null != audioData) {
  64. L.i("讯飞SDK开始音频流识别");
  65. mIat.writeAudio(audioData, 0, audioData.length);
  66. mIat.stopListening();
  67. } else {
  68. mIat.cancel();
  69. Toast.makeText(ChatActivity.this, "讯飞SDK读取音频流失败", Toast.LENGTH_SHORT).show();
  70. }
  71. }
  72. }
  73. });

发送三条消息

  1. public class SendMsgUtils {
  2. /**
  3. * 发送语音、Gif图等通用聊天消息
  4. *
  5. * @param activity 必须是ChatActivity或ChatActivityPrivate
  6. * @param msgType 例如Common.e_MsgType.MSGTYPE_GIF_VALUE
  7. * @param content 消息内容,一般都是json格式
  8. */
  9. public static void sendNormalMsg(Activity activity, int msgType, String content, long clientid) {
  10. if (activity instanceof ChatActivity) {
  11. if (msgType == Common.e_MsgType.MSGTYPE_VOICE_VALUE || msgType == Common.e_MsgType.MSGTYPE_GIF_VALUE) {
  12. int roleType = ((ChatActivity) activity).getRoleType();
  13. if (roleType <= 0) {//游客
  14. Toast.makeText(activity, "您没有权限发送此类型消息", Toast.LENGTH_SHORT).show();
  15. return;
  16. }
  17. }
  18. }
  19. int groupId = 0;
  20. boolean isGroup = false;
  21. int dstId = 0;
  22. if (activity instanceof ChatActivity) {
  23. groupId = ((ChatActivity) activity).getGroupId();
  24. isGroup = ((ChatActivity) activity).isGroup();
  25. dstId = ((ChatActivity) activity).getDstId();
  26. } else if (activity instanceof ChatActivityPrivate) {
  27. groupId = ((ChatActivityPrivate) activity).getGroupId();
  28. isGroup = ((ChatActivityPrivate) activity).isGroup();
  29. dstId = ((ChatActivityPrivate) activity).getDstId();
  30. }
  31. ChatReqHelper.sendNormalMsg(content, groupId, isGroup, dstId, clientid);
  32. ChatModel model = new ChatModel(content, ChatModel.RIGHT, msgType);
  33. model.clientid = clientid;
  34. model.setMsgTime((int) (System.currentTimeMillis() / 1000));
  35. model.setGroupId(groupId);
  36. model.setSrcUser(ChatReqHelper.getUserInfo());
  37. if (activity instanceof ChatActivity) {
  38. ((ChatActivity) activity).getmGroupChats().add(0, model);
  39. ((ChatActivity) activity).getmRvSquareChat().getAdapter().notifyItemInserted(0);
  40. ((ChatActivity) activity).getmRvSquareChat().scrollToPosition(0);
  41. } else if (activity instanceof ChatActivityPrivate) {
  42. Common.UserInfo_t dstUserInfo = Common.UserInfo_t.getDefaultInstance().newBuilderForType().setUserId(dstId).build();
  43. model.setDstUser(dstUserInfo);
  44. ((ChatActivityPrivate) activity).getmGroupChats().add(0, model);
  45. ((ChatActivityPrivate) activity).getmRvSquareChat().getAdapter().notifyItemInserted(0);
  46. ((ChatActivityPrivate) activity).getmRvSquareChat().scrollToPosition(0);
  47. }
  48. }
  49. /**
  50. * 发送语音消息(里面只有语音的URL路径及时长),里面用的完全就是sendNormalMsg的逻辑
  51. *
  52. * @param activity 必须是ChatActivity或ChatActivityPrivate
  53. * @param recordBean 封装所有信息的bean
  54. */
  55. public static void sendNormalVoiceMsg(Activity activity, RecordBean recordBean) {
  56. //当上传七牛成功后发送一条socket消息,当收到响应时(会返回一个msgid)说明此socket消息发送成功了
  57. int duration = (int) ((recordBean.endTime - recordBean.startTime) / 1000);
  58. RecordBean.VoiceBean voiceBean = new RecordBean.VoiceBean(recordBean.qnKey, duration);
  59. String content = new Gson().toJson(voiceBean);//{"path:":"", duration:100}
  60. sendNormalMsg(activity, Common.e_MsgType.MSGTYPE_VOICE_VALUE, content, recordBean.clientid);
  61. L.i("发送语音消息。content=" + content + "。clientid=" + recordBean.clientid);
  62. }
  63. /**
  64. * 发送语音所对应的文本消息,目的是将上传到七牛的【语音】和讯飞翻译的【内容】关联起来
  65. */
  66. public static void sendVoiceContentMsg(Activity activity, RecordBean recordBean) {
  67. if (recordBean == null) return;
  68. if (recordBean.sendVoiceSuccess && recordBean.translateSuccess && recordBean.msgid != 0) {
  69. int groupId = 0, dstId = 0;
  70. if (activity instanceof ChatActivity) {
  71. groupId = ((ChatActivity) activity).getGroupId();
  72. dstId = ((ChatActivity) activity).getDstId();
  73. } else if (activity instanceof ChatActivityPrivate) {
  74. groupId = ((ChatActivityPrivate) activity).getGroupId();
  75. dstId = ((ChatActivityPrivate) activity).getDstId();
  76. }
  77. ChatSvr.CMDVoiceContentSubmit message = ChatSvr.CMDVoiceContentSubmit.newBuilder()
  78. .setGroupid(groupId)//群id
  79. .setSrcuid(AccountManager.getInstance().getServiceUserId())//消息发起者id
  80. .setDstuid(dstId)//消息接收者id(群聊传0)
  81. .setMsgid(recordBean.msgid)//msgid,此msgid是由服务器返回的
  82. .setContent(recordBean.content)//语音消息翻译成文本的内容
  83. .build();
  84. LoginConnection.voiceContentSubmit(message.toByteArray(), null);
  85. L.i("发送语音文本消息。content=" + recordBean.content + "。msgid=" + recordBean.msgid);
  86. if (recordBean.pcmPath != null && new File(recordBean.pcmPath).delete()) L.i("成功删除pcm临时文件");
  87. if (recordBean.aacPath != null && new File(recordBean.aacPath).delete()) L.i("成功删除aac临时文件");
  88. //移除集合中的此RecordBean
  89. LongSparseArray<RecordBean> recordList = null;
  90. if (activity instanceof ChatActivity) recordList = ((ChatActivity) activity).recordList;
  91. else if (activity instanceof ChatActivityPrivate) recordList = ((ChatActivityPrivate) activity).recordList;
  92. if (recordList != null) {
  93. recordList.remove(recordBean.clientid);
  94. L.i("成功移除recordBean:" + recordBean.clientid);
  95. }
  96. } else L.i("RecordBean状态错误" + recordBean.toString());
  97. }
  98. /**
  99. * 查询语音文本消息。这时已经与RecordBeanme没有任何关系了,这里的msgid是后台返回的
  100. */
  101. public static void qryVoiceContentMsg(Activity activity, int msgid) {
  102. int groupId = 0, dstId = 0;
  103. if (activity instanceof ChatActivity) {
  104. groupId = ((ChatActivity) activity).getGroupId();
  105. dstId = ((ChatActivity) activity).getDstId();
  106. } else if (activity instanceof ChatActivityPrivate) {
  107. groupId = ((ChatActivityPrivate) activity).getGroupId();
  108. dstId = ((ChatActivityPrivate) activity).getDstId();
  109. }
  110. ChatSvr.CMDQryChatMsgReq req = ChatSvr.CMDQryChatMsgReq.newBuilder()
  111. .setGroupid(groupId)//群id
  112. .setSrcuid(AccountManager.getInstance().getServiceUserId())//消息发起者id
  113. .setDstuid(dstId)//消息接收者id(群聊传0)
  114. .setMsgid(msgid)//msgid
  115. .build();
  116. LoginConnection.qryChatMsgReq(req.toByteArray(), null);
  117. L.i("查询语音文本消息。msgid=" + msgid + "。groupId=" + groupId);
  118. }
  119. }

服务器的两个响应

  1. public void onEventMainThread(BaseEvent event) {
  2. if (event.getEvent() == EventHelper.EVENT_GROUPMSGRECV) {
  3. if (event.getData() instanceof ChatSvr.CMDGroupMsgRecv) {
  4. //异步上传语音到七牛成功后,客户端发送一条socket消息通知语音的URL路径
  5. //这是当服务器收到此消息后做出的响应,我们主要是拿到回一个msgid,以后就可以根据msgid通知语音对应的文本了
  6. ChatSvr.CMDGroupMsgRecv msgRecv = (ChatSvr.CMDGroupMsgRecv) event.getData();
  7. for (int i = 0; i < recordList.size(); i++) {
  8. Long key = recordList.keyAt(i);
  9. if (msgRecv.getClientMSgId() == key) {//RecordBean的clientid和recordList的key时同一个数
  10. RecordBean recordBean = recordList.get(key);
  11. recordBean.sendVoiceSuccess = true;
  12. recordBean.msgid = msgRecv.getMsgId();
  13. L.i("发送语音响应:msgid=" + recordBean.msgid + " clientid=" + recordBean.clientid);
  14. //**************************【将上传到七牛的语音和讯飞翻译的内容关联起来】******************************
  15. //如果第二个异步已经完成,则直接发一条socket消息,将两者内容联系在一起;否则等第二个异步完成以后再做处理
  16. if (recordBean.translateSuccess) SendMsgUtils.sendVoiceContentMsg(this, recordBean);
  17. break;
  18. }
  19. }
  20. }
  21. return;
  22. }
  23. if (event.getEvent() == EventHelper.EVENT_QRY_CHAT_MSG) {
  24. //**********************************************【响应查询语音消息】************************************************
  25. if (event.getData() instanceof ChatSvr.CMDQryChatMsgResp) {
  26. ChatSvr.CMDQryChatMsgResp resp = (ChatSvr.CMDQryChatMsgResp) event.getData();
  27. if (resp.getMsg() != null) {
  28. int msgID = resp.getMsg().getMsgId();
  29. String voiceContent;
  30. if (resp.getMsg().getVoiceContent() != null) voiceContent = resp.getMsg().getVoiceContent().getData();
  31. else voiceContent = "翻译失败";
  32. Toast.makeText(this, "msgID:" + msgID + " 内容:" + voiceContent, Toast.LENGTH_SHORT).show();
  33. L.i("查询语音文本响应。" + "msgID:" + msgID + " 内容:" + voiceContent);
  34. }
  35. }
  36. return;
  37. }
  38. }

讯飞监听、上传语音等

  1. public class VoiceUtils {
  2. public interface OnRecognizerResultListener {
  3. void onResult(String content);
  4. }
  5. public static RecognizerListener getRecognizerListener(OnRecognizerResultListener listener) {
  6. return new RecognizerListener() {
  7. StringBuilder sb = new StringBuilder();
  8. @Override
  9. public void onBeginOfSpeech() {// 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入
  10. L.i("onBeginOfSpeech");
  11. }
  12. @Override
  13. public void onError(SpeechError error) {
  14. L.i("onError:" + error.getErrorCode() + " " + error.getErrorDescription());
  15. //http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=13056&fromuid=44990
  16. // 错误码:10118(您没有说话),可能是录音机权限被禁,需要提示用户打开应用的录音权限。
  17. // 如果使用本地功能(语记)需要提示用户开启语记的录音权限。
  18. //没有数据,vad_bos设置问题
  19. }
  20. @Override
  21. public void onEndOfSpeech() {// 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入
  22. L.i("onEndOfSpeech");
  23. }
  24. @Override
  25. public void onResult(RecognizerResult results, boolean isLast) {
  26. L.i("onResult:" + results.getResultString() + "。格式化内容:" + getFormatResult(results) + "。isLast:" + isLast);
  27. sb.append(getFormatResult(results));
  28. if (isLast) {
  29. L.i("完整结果为:" + sb.toString());
  30. listener.onResult(sb.toString());
  31. }
  32. }
  33. @Override
  34. public void onVolumeChanged(int volume, byte[] data) {
  35. L.i("onVolumeChanged。当前正在说话,音量大小:" + volume + "。返回音频数据长度:" + data.length);
  36. }
  37. @Override
  38. public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {
  39. // 以下代码用于获取与云端的会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因
  40. // 若使用本地能力,会话id为null
  41. if (SpeechEvent.EVENT_SESSION_ID == eventType) {
  42. String sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);
  43. L.i("onEvent:" + eventType + " session_id =" + sid);
  44. }
  45. }
  46. };
  47. }
  48. public static SpeechRecognizer getSpeechRecognizerInstance(Context context) {
  49. //*****************************************************初始化监听器************************************************
  50. InitListener mInitListener = code -> {
  51. if (code != ErrorCode.SUCCESS) L.i("初始化失败,错误码:" + code);
  52. };
  53. SpeechRecognizer mIat = SpeechRecognizer.createRecognizer(context, mInitListener);
  54. if (null == mIat) {
  55. // 创建单例失败,与 21001 错误为同样原因,参考 http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=9688
  56. L.i("创建对象失败,请确认 libmsc.so 放置正确,且有调用 createUtility 进行初始化");
  57. Toast.makeText(context, "语音引擎初始化失败", Toast.LENGTH_SHORT).show();
  58. return null;
  59. }
  60. //******************************************************参数设置**************************************************
  61. // 清空参数
  62. mIat.setParameter(SpeechConstant.PARAMS, null);
  63. mIat.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);// 设置听写引擎
  64. mIat.setParameter(SpeechConstant.RESULT_TYPE, "json");// 设置返回结果格式
  65. mIat.setParameter(SpeechConstant.LANGUAGE, "zh_cn");// 设置语言
  66. mIat.setParameter(SpeechConstant.ACCENT, "mandarin");// 设置语言区域,mandarin为普通话;
  67. // 设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理
  68. mIat.setParameter(SpeechConstant.VAD_BOS, "4000");//4000
  69. // 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入, 自动停止录音
  70. mIat.setParameter(SpeechConstant.VAD_EOS, "4000");//1000
  71. // 设置标点符号,设置为"0"返回结果无标点,设置为"1"返回结果有标点
  72. mIat.setParameter(SpeechConstant.ASR_PTT, "1");
  73. // 设置音频【保存】路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
  74. mIat.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");// 注:AUDIO_FORMAT参数语记需要更新版本才能生效
  75. mIat.setParameter(SpeechConstant.ASR_AUDIO_PATH, RecordBean.XF_VOICE_FILE_PATH);
  76. // 设置音频来源为外部文件
  77. mIat.setParameter(SpeechConstant.AUDIO_SOURCE, "-1");
  78. // 也可以像以下这样直接设置音频文件路径识别(要求设置文件在sdcard上的全路径):
  79. //mIat.setParameter(SpeechConstant.AUDIO_SOURCE, "-2");
  80. //mIat.setParameter(SpeechConstant.ASR_SOURCE_PATH, "sdcard/XXX/XXX.pcm");
  81. return mIat;
  82. }
  83. /**
  84. * 获取解析出的字符串内容
  85. */
  86. private static String getFormatResult(RecognizerResult results) {
  87. String text = parseIatResult(results.getResultString());
  88. String sn = null;
  89. // 读取json结果中的sn字段
  90. try {
  91. JSONObject resultJson = new JSONObject(results.getResultString());
  92. sn = resultJson.optString("sn");
  93. } catch (JSONException e) {
  94. e.printStackTrace();
  95. }
  96. // 用HashMap存储听写结果
  97. HashMap<String, String> mIatResults = new LinkedHashMap<>();
  98. mIatResults.put(sn, text);
  99. StringBuilder resultBuffer = new StringBuilder();
  100. for (String key : mIatResults.keySet()) {
  101. resultBuffer.append(mIatResults.get(key));
  102. }
  103. return resultBuffer.toString();
  104. }
  105. private static String parseIatResult(String json) {
  106. StringBuilder ret = new StringBuilder();
  107. try {
  108. JSONArray words = new JSONObject(new JSONTokener(json)).getJSONArray("ws");
  109. for (int i = 0; i < words.length(); i++) {
  110. // 转写结果词,默认使用第一个结果
  111. JSONArray items = words.getJSONObject(i).getJSONArray("cw");
  112. JSONObject obj = items.getJSONObject(0);
  113. ret.append(obj.getString("w"));
  114. }
  115. } catch (Exception e) {
  116. e.printStackTrace();
  117. }
  118. return ret.toString();
  119. }
  120. /**
  121. * 上传语音消息,然后发送语音消息
  122. */
  123. public static void uploadVoiceAndSendMsg(BaseActivity activity, RecordBean recordBean) {
  124. if (recordBean == null || recordBean.aacPath == null) return;
  125. final File file = new File(recordBean.aacPath);
  126. if (!file.exists()) {
  127. ToastHelper.showErrorToast("录音文件不存在");
  128. return;
  129. }
  130. if (AccountManager.getInstance().getServiceInfo() == null) {
  131. ToastHelper.showToastInThread("您尚未登录");
  132. return;
  133. }
  134. activity.showLoadingDialog();
  135. //**************************************************上传语音消息****************************************
  136. String keyQN = LoginConnection.getUploadKey(recordBean.aacPath);
  137. HBApplication.getInstance().getUploadManager()
  138. .put(recordBean.aacPath,
  139. keyQN,
  140. LoginConnection.getUploadToken(),
  141. (key, info, res) -> {
  142. L.i("上传语音骑牛key:" + key);
  143. //*************************************发送语音消息*****************************************
  144. //当上传七牛成功后发送一条socket消息,当收到响应时(会返回一个msgid)说明此socket消息发送成功了
  145. recordBean.qnKey = key;
  146. SendMsgUtils.sendNormalVoiceMsg(activity, recordBean);
  147. },
  148. new UploadOptions(null, null, false, (key, percent) -> {
  149. //if (activity.dialog != null) activity.dialog.update((int) percent * 100);
  150. }, null));
  151. }
  152. /**
  153. * 【读】SD卡也即/mnt/sdcard/目录下的文件
  154. */
  155. public static byte[] readSDFile(String fileName) {
  156. if (TextUtils.isEmpty(fileName)) return null;
  157. File file = new File(fileName);
  158. if (!file.exists()) return null;
  159. try {
  160. InputStream ins = new FileInputStream(file);
  161. byte[] data = new byte[ins.available()];
  162. ins.read(data);
  163. ins.close();
  164. return data;
  165. } catch (Exception e) {
  166. e.printStackTrace();
  167. }
  168. return null;
  169. }
  170. }

日志

发送语音
  1. I/RecordBean: 包青天bqt(RecordBean.java:39)#<init>【语音文件保存路径:/storage/emulated/0/voice/pcm_2017_07_04 17_05_23 100.pcm
  2. /storage/emulated/0/voice/aac_2017_07_04 17_05_23 100.aac】
  3. I/ChatActivity$5: 包青天bqt(ChatActivity.java:908)#pressStop【讯飞SDK开始音频流识别】
  4. I/VoiceUtils$1: 包青天bqt(VoiceUtils.java:97)#onVolumeChanged【onVolumeChanged。当前正在说话,音量大小:0。返回音频数据长度:33280】
  5. I/VoiceUtils$1: 包青天bqt(VoiceUtils.java:81)#onEndOfSpeech【onEndOfSpeech】
  6. I/VoiceUtils$1: 包青天bqt(VoiceUtils.java:106)#onEvent【onEvent:20001 session_id =iatad118bff@gz02600cb6a6553cf300】
  7. I/VoiceUtils$1: 包青天bqt(VoiceUtils.java:86)#onResult【onResult:{"sn":1,"ls":false,"bg":0,"ed":0,"ws":[{"bg":0,"cw":[{"sc":0.00,"w":"你好"}]}]}。格式化内容:你好。isLast:false】
  8. I/VoiceUtils$1: 包青天bqt(VoiceUtils.java:106)#onEvent【onEvent:20001 session_id =iatad118bff@gz02600cb6a6553cf300】
  9. I/VoiceUtils: 包青天bqt(VoiceUtils.java:222)#lambda$uploadVoiceAndSendMsg$1【上传语音骑牛key:_F-UBAN-YeQIB.aac】
  10. I/SendMsgUtils: 包青天bqt(SendMsgUtils.java:87)#sendNormalVoiceMsg【发送语音消息。content={"duration":1,"path":"_F-UBAN-YeQIB.aac"}。clientid=1499159123100】
  11. I/VoiceUtils$1: 包青天bqt(VoiceUtils.java:86)#onResult【onResult:{"sn":2,"ls":true,"bg":0,"ed":0,"ws":[{"bg":0,"cw":[{"sc":0.00,"w":"。"}]}]}。格式化内容:。。isLast:true】
  12. I/VoiceUtils$1: 包青天bqt(VoiceUtils.java:89)#onResult【完整结果为:你好。】
  13. I/ChatActivity: 包青天bqt(ChatActivity.java:1703)#onEventMainThread【发送语音响应:msgid=13878 clientid=1499159123100】
  14. I/SendMsgUtils: 包青天bqt(SendMsgUtils.java:113)#sendVoiceContentMsg【发送语音文本消息。content=你好。。msgid=13878】
  15. I/SendMsgUtils: 包青天bqt(SendMsgUtils.java:115)#sendVoiceContentMsg【成功删除pcm临时文件】
  16. I/SendMsgUtils: 包青天bqt(SendMsgUtils.java:116)#sendVoiceContentMsg【成功删除aac临时文件】
  17. I/SendMsgUtils: 包青天bqt(SendMsgUtils.java:124)#sendVoiceContentMsg【成功移除recordBean:1499159123100】
查询语音对应文字
  1. I/SendMsgUtils: 包青天bqt(SendMsgUtils.java:148)#qryVoiceContentMsg【查询语音文本消息。msgid=13879。groupId=36】
  2. I/ChatActivity: 包青天bqt(ChatActivity.java:1723)#onEventMainThread【查询语音文本响应。msgID:13879 内容:需要。】
2017-7-4

发送语音+讯飞翻译 项目案例相关推荐

  1. php 讯飞语音评测_人工AI语音新高度:讯飞翻译机3.0测评体验

    科大讯飞在人工AI语音方面的地位都是毋庸置疑的,牛不牛就不多说了,说得高大上的东西离我们普通用户又太遥远,就说说离我们近的东西-语言,语言是人类交流的桥梁,生活中,工作中都离不开,但是每个地方有方言, ...

  2. 科大讯飞胡郁率队日内瓦签约 4月20日讯飞翻译机2.0正式发布

    近日,科大讯飞与国际电信联盟(简称国际电联或ITU)敲定战略合作协议,正式签约仪式于瑞士时间4月17日上午在日内瓦举行.工业和信息化部总工程师陈因.国际电信联盟秘书长赵厚麟.科大讯飞执行总裁胡郁等出席 ...

  3. 百度AI全面开放EasyDL开启AI普及化时代;讯飞翻译机2.0发布:支持34种语言;华为开发能读懂人类情绪的AI技术

    百度AI全面开放EasyDL开启AI普及化时代,"小白"也能用AI 百度"燎原计划2018"暨百度AI开发者实战营第二季强势回归,首站北京即放送三大满足各类开发 ...

  4. 科大讯飞举办翻译战略暨新品发布会,讯飞翻译机2.0正式上市

    2018年4月20日下午,"世界聊得来"科大讯飞翻译战略暨新品上市发布会在北京亮马桥外交办公大楼盛大举行.科大讯飞的厂商代表.渠道合作伙伴.特邀嘉宾以及媒体朋友等数百名用户共同见证 ...

  5. “能听会说”畅行全球,讯飞翻译机让沟通无国界

    随着"十一"小长假的临近,出境游再次成为网友们茶余饭后的热门话题.如今,网友们向往的出境游形态已不再是"上车睡觉.下车拍照"--传统的跟团游玩模式逐渐被个性化. ...

  6. 讯飞翻译战略强势发布 助力人类语言大互通

    博鳌亚洲论坛上讯飞翻译机2.0首次亮相便引起大众热议,新品尚未揭开"庐山真面目",却已红遍全论坛.4月20日,迎来了"科大讯飞翻译战略暨新品上市发布会",惊艳亮 ...

  7. 匹马抢三关:讯飞翻译机3.0的破障之战

    技术和市场的两股气旋,正在催促翻译机加速奔跑. 根据中国旅游研究院统计,2018年全年中国出境游旅客达到1.4亿人次,比2017上涨1100万,同比增幅达到了13.5%.时至今日,我们已经可以确凿地说 ...

  8. 讯飞翻译机 2.0 拆机

    头脑发热买了一个讯飞翻译机的2.0版本,不要问我为什么拆,因为它的功能实在是... 从后盖来看无任何螺丝孔,于是乎觉得可能是从前面屏幕开始,于是把前面的屏幕处拆开,发现里面也没有螺丝孔和卡槽. 尝试从 ...

  9. 2019输入序列号闪退_正式告别60秒长语音 讯飞输入法新版评测:能动口我绝不动手...

    一.前言:60秒长语音有希望消灭了 从2010年,讯飞推出业界首个中文语音输入法,将语音输入带到大众面前直到今天,语音输入早就不再是当初功能机时代"语音王"的土味儿模样.如今,语音 ...

最新文章

  1. JVM - CMS深度剖析
  2. Android打包的那些事
  3. 关于intel 32 hex文件格式以及hex2rom.sed
  4. DotLiquid模板引擎简介
  5. 当前元素_前端系列——获取页面中的DOM元素
  6. Java集合框架-重构设计
  7. 13.JAVA之GUI编程将程序打包jar
  8. android 静态编译链接,Android NDK:使用预编译的静态库链接
  9. Flink 在人工智能领域的应用实践
  10. 软件无线电的发展与展望
  11. 网易云音乐android变臃肿,网易云音乐版权少,为什么用户还能突破8亿??
  12. 使用SmartUpload上传文件报错
  13. MC74HC595驱动
  14. iOS开发基础-序列帧动画之Tom猫
  15. idr帧 i帧 p帧_GOP、IDR帧、I帧周期的关系
  16. Day10.事件下(学习日记10)
  17. onenote文本每一行出现绿色背景和旁边出现作者信息的完美解决方法
  18. 打码指南-由猫眼线下扫码1分购谈起 | 掘金直播 小程序专场分享总结
  19. 劝你裸辞和创业的人,非傻即坏!
  20. vue实例下的computed什么时候执行?

热门文章

  1. cad卸载_30年CAD老师傅一步步教你怎么把CAD卸载得一干二净
  2. 记一次递归在我项目中所发挥的作用
  3. android 记一次富文本加载之路
  4. python的类的super()
  5. 【iCore4 双核心板_FPGA】例程七:状态机实验——状态机使用
  6. python魔法方法详解
  7. Ansible自动化运维笔记1(安装配置)
  8. MongoDB_限制集(Capped)
  9. 千万PV级别WEB站点架构设计
  10. shell按行读取文件的常见几种方法