每日冲刺4
继续语音功能
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <!--标题--> <com.google.android.material.appbar.MaterialToolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="@color/white" app:navigationIcon="@mipmap/icon_back"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="语音输入" android:textColor="@color/black" android:textSize="18sp" /> </com.google.android.material.appbar.MaterialToolbar> <!--列表--> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rv_result" android:layout_width="match_parent" android:layout_height="match_parent" android:overScrollMode="never" android:scrollbars="none" /> </LinearLayout> <!--浮动按钮 语音搜索--> <com.google.android.material.floatingactionbutton.FloatingActionButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="@dimen/dp_20" android:onClick="voiceInput" android:src="@mipmap/icon_voice" app:backgroundTint="@color/white" app:backgroundTintMode="screen" app:fabSize="auto" app:hoveredFocusedTranslationZ="@dimen/dp_18" app:pressedTranslationZ="@dimen/dp_18" /> </FrameLayout>
package com.llw.goodtrash.ui; import android.os.Bundle; import android.view.View; import com.google.android.material.appbar.MaterialToolbar; import com.llw.goodtrash.R; import com.llw.mvplibrary.base.BaseActivity; /** * 语音输入物品进行垃圾分类 */ public class VoiceInputActivity extends BaseActivity { private MaterialToolbar toolbar; @Override public void initData(Bundle savedInstanceState) { initView(); } private void initView() { setStatubar(this, R.color.white, true); toolbar = findViewById(R.id.toolbar); back(toolbar, false); } @Override public int getLayoutId() { return R.layout.activity_voice_input; } public void voiceInput(View view) { showMsg("语音"); } }
/** * 请求权限 */ @SuppressLint("CheckResult") private void requestPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { //Android6.0及以上版本 rxPermissions.request(Manifest.permission.RECORD_AUDIO).subscribe(granted -> { if (granted) {//权限通过 //开始语音监听 } else {//权限未通过 showMsg("权限未通过,你不能使用该功能"); } }); } else { //Android6.0以下无须动态请求 //开始语音监听 } }
package com.llw.goodtrash.utils; import org.json.JSONArray; import org.json.JSONObject; import org.json.JSONTokener; /** * Json结果解析类 * * @author llw * @date 2021/4/1 15:44 */ public class JsonParser { public static String parseIatResult(String json) { StringBuffer ret = new StringBuffer(); try { JSONTokener tokener = new JSONTokener(json); JSONObject joResult = new JSONObject(tokener); JSONArray words = joResult.getJSONArray("ws"); for (int i = 0; i < words.length(); i++) { // 转写结果词,默认使用第一个结果 JSONArray items = words.getJSONObject(i).getJSONArray("cw"); JSONObject obj = items.getJSONObject(0); ret.append(obj.getString("w")); // 如果需要多候选结果,解析数组其他字段 // for(int j = 0; j < items.length(); j++) // { // JSONObject obj = items.getJSONObject(j); // ret.append(obj.getString("w")); // } } } catch (Exception e) { e.printStackTrace(); } return ret.toString(); } public static String parseGrammarResult(String json) { StringBuffer ret = new StringBuffer(); try { JSONTokener tokener = new JSONTokener(json); JSONObject joResult = new JSONObject(tokener); JSONArray words = joResult.getJSONArray("ws"); for (int i = 0; i < words.length(); i++) { JSONArray items = words.getJSONObject(i).getJSONArray("cw"); for(int j = 0; j < items.length(); j++) { JSONObject obj = items.getJSONObject(j); if(obj.getString("w").contains("nomatch")) { ret.append("没有匹配结果."); return ret.toString(); } ret.append("【结果】" + obj.getString("w")); ret.append("【置信度】" + obj.getInt("sc")); ret.append("\n"); } } } catch (Exception e) { e.printStackTrace(); ret.append("没有匹配结果."); } return ret.toString(); } public static String parseLocalGrammarResult(String json) { StringBuffer ret = new StringBuffer(); try { JSONTokener tokener = new JSONTokener(json); JSONObject joResult = new JSONObject(tokener); JSONArray words = joResult.getJSONArray("ws"); for (int i = 0; i < words.length(); i++) { JSONArray items = words.getJSONObject(i).getJSONArray("cw"); for(int j = 0; j < items.length(); j++) { JSONObject obj = items.getJSONObject(j); if(obj.getString("w").contains("nomatch")) { ret.append("没有匹配结果."); return ret.toString(); } ret.append("【结果】" + obj.getString("w")); ret.append("\n"); } } ret.append("【置信度】" + joResult.optInt("sc")); } catch (Exception e) { e.printStackTrace(); ret.append("没有匹配结果."); } return ret.toString(); } public static String parseTransResult(String json, String key) { StringBuffer ret = new StringBuffer(); try { JSONTokener tokener = new JSONTokener(json); JSONObject joResult = new JSONObject(tokener); String errorCode = joResult.optString("ret"); if(!errorCode.equals("0")) { return joResult.optString("errmsg"); } JSONObject transResult = joResult.optJSONObject("trans_result"); ret.append(transResult.optString(key)); /*JSONArray words = joResult.getJSONArray("results"); for (int i = 0; i < words.length(); i++) { JSONObject obj = words.getJSONObject(i); ret.append(obj.getString(key)); }*/ } catch (Exception e) { e.printStackTrace(); } return ret.toString(); } }
package com.llw.goodtrash.utils; import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.graphics.Paint; import android.os.Bundle; import android.os.Environment; import android.os.MemoryFile; import android.util.Log; import android.widget.TextView; import android.widget.Toast; import com.iflytek.cloud.ErrorCode; import com.iflytek.cloud.InitListener; import com.iflytek.cloud.RecognizerResult; import com.iflytek.cloud.SpeechConstant; import com.iflytek.cloud.SpeechError; import com.iflytek.cloud.SpeechEvent; import com.iflytek.cloud.SpeechRecognizer; import com.iflytek.cloud.SpeechSynthesizer; import com.iflytek.cloud.SynthesizerListener; import com.iflytek.cloud.msc.util.FileUtil; import com.iflytek.cloud.msc.util.log.DebugLog; import com.iflytek.cloud.ui.RecognizerDialog; import com.iflytek.cloud.ui.RecognizerDialogListener; import org.json.JSONException; import org.json.JSONObject; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Objects; import java.util.Vector; /** * 语音工具类 * * @author llw */ public class SpeechUtil { private static final String TAG = "SpeechUtil"; private static Context mContext; // 语音合成对象 public static SpeechSynthesizer mTts; //播放的文字 private static String defaultText = "富强、明主、文明、和谐、自由、平等、公正、法制、爱国、敬业、诚信、友善。"; // 引擎类型 private static String mEngineType = SpeechConstant.TYPE_CLOUD; private static Vector<byte[]> container = new Vector<>(); //内存文件 private static MemoryFile memoryFile; //总大小 public static volatile long mTotalSize = 0; // 默认发音人 private static String voicer = "xiaoyan"; //语速 private static String speedValue = "50"; //音调 private static String pitchValue = "50"; //音量 private static String volumeValue = "50"; private static TextView tvState; /****************语音识别********************/ private static SpeechRecognizer mIat;// 语音听写对象 private static RecognizerDialog mIatDialog;// 语音听写UI // 用HashMap存储听写结果 private static HashMap<String, String> mIatResults = new LinkedHashMap<String, String>(); private static SharedPreferences mSharedPreferences;//缓存 private static String language = "zh_cn";//识别语言 private static String resultType = "json";//结果内容数据格式 private static String dictationResults;//听写结果 /** * 初始化监听。 */ private static InitListener mTtsInitListener = code -> { Log.i(TAG, "InitListener init() code = " + code); if (code != ErrorCode.SUCCESS) { Log.i(TAG, "初始化失败,错误码:" + code); } else { Log.i(TAG, "初始化成功"); } }; /** * 初始化 * * @param context */ public static void init(Context context) { mContext = context; /*************** 语音合成 *****************/ // 初始化合成对象 mTts = SpeechSynthesizer.createSynthesizer(mContext, mTtsInitListener); /*************** 语音听写 *****************/ // 使用SpeechRecognizer对象,可根据回调消息自定义界面; mIat = SpeechRecognizer.createRecognizer(mContext, mInitListener); // 使用UI听写功能,请根据sdk文件目录下的notice.txt,放置布局文件和图片资源 mIatDialog = new RecognizerDialog(mContext, mInitListener); mSharedPreferences = mContext.getSharedPreferences("ASR", Activity.MODE_PRIVATE); } /** * 合成回调监听。 */ private static SynthesizerListener mTtsListener = new SynthesizerListener() { //开始播放 @Override public void onSpeakBegin() { Log.i(TAG, "开始播放"); } //暂停播放 @Override public void onSpeakPaused() { Log.i(TAG, "暂停播放"); } //继续播放 @Override public void onSpeakResumed() { Log.i(TAG, "继续播放"); } //合成进度 @Override public void onBufferProgress(int percent, int beginPos, int endPos, String info) { Log.i(TAG, "合成进度:" + percent + "%"); } //播放进度 @Override public void onSpeakProgress(int percent, int beginPos, int endPos) { // 播放进度 Log.i(TAG, "播放进度:" + percent + "%"); tvState.setText("播报中"); } //播放完成 @Override public void onCompleted(SpeechError error) { tvState.setText("播报完成"); if (error == null) { Log.i(TAG, "播放完成," + container.size()); DebugLog.LogD("播放完成," + container.size()); for (int i = 0; i < container.size(); i++) { //写入文件 writeToFile(container.get(i)); } //保存文件 FileUtil.saveFile(memoryFile, mTotalSize, mContext.getExternalFilesDir(null) + "/1.pcm"); } else { //异常信息 showTip(error.getPlainDescription(true)); } tvState.setText(""); } //事件 @Override public void onEvent(int eventType, int arg1, int arg2, Bundle obj) { // 以下代码用于获取与云端的会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因 // 若使用本地能力,会话id为null if (SpeechEvent.EVENT_SESSION_ID == eventType) { String sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID); Log.i(TAG, "session id =" + sid); } //当设置SpeechConstant.TTS_DATA_NOTIFY为1时,抛出buf数据 if (SpeechEvent.EVENT_TTS_BUFFER == eventType) { byte[] buf = obj.getByteArray(SpeechEvent.KEY_EVENT_TTS_BUFFER); Log.i(TAG, "bufis =" + buf.length); container.add(buf); } } }; /** * 写入文件 */ private static void writeToFile(byte[] data) { if (data == null || data.length == 0) { return; } try { if (memoryFile == null) { Log.i(TAG, "memoryFile is null"); String mFilepath = mContext.getExternalFilesDir(null) + "/1.pcm"; memoryFile = new MemoryFile(mFilepath, 1920000); memoryFile.allowPurging(false); } memoryFile.writeBytes(data, 0, (int) mTotalSize, data.length); mTotalSize += data.length; } catch (Exception e) { e.printStackTrace(); } } /** * 参数设置 * * @return */ private static void setParam() { // 清空参数 mTts.setParameter(SpeechConstant.PARAMS, null); // 根据合成引擎设置相应参数 if (mEngineType.equals(SpeechConstant.TYPE_CLOUD)) { mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD); //支持实时音频返回,仅在synthesizeToUri条件下支持 mTts.setParameter(SpeechConstant.TTS_DATA_NOTIFY, "1"); // 设置在线合成发音人 mTts.setParameter(SpeechConstant.VOICE_NAME, voicer); //设置合成语速 mTts.setParameter(SpeechConstant.SPEED, speedValue); //设置合成音调 mTts.setParameter(SpeechConstant.PITCH, pitchValue); //设置合成音量 mTts.setParameter(SpeechConstant.VOLUME, volumeValue); } else { mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL); mTts.setParameter(SpeechConstant.VOICE_NAME, ""); } // 设置播放合成音频打断音乐播放,默认为true mTts.setParameter(SpeechConstant.KEY_REQUEST_FOCUS, "false"); // 设置音频保存路径,保存音频格式支持pcm、wav mTts.setParameter(SpeechConstant.AUDIO_FORMAT, "pcm"); mTts.setParameter(SpeechConstant.TTS_AUDIO_PATH, mContext.getExternalFilesDir(null) + "/msc/tts.pcm"); } /** * 开始语音预报 */ public static void startVoiceBroadcast(String text, TextView textView) { tvState = textView; if (mTts == null) { showTip("创建对象失败,请确认 libmsc.so 放置正确,且有调用 createUtility 进行初始化"); return; } if (text == null || text.isEmpty()) { text = defaultText; } //设置参数 setParam(); //开始合成播放 int code = mTts.startSpeaking(text, mTtsListener); if (code != ErrorCode.SUCCESS) { showTip("语音合成失败,错误码: " + code); } } /** * 初始化语音听写监听器 */ private static InitListener mInitListener = code -> { Log.d(TAG, "SpeechRecognizer init() code = " + code); if (code != ErrorCode.SUCCESS) { showTip("初始化失败,错误码:" + code + ",请点击网址https://www.xfyun.cn/document/error-code查询解决方案"); } }; /** * 听写UI监听器 */ private static RecognizerDialogListener mRecognizerDialogListener = new RecognizerDialogListener() { /** * 识别结果 */ @Override public void onResult(RecognizerResult results, boolean isLast) { parsingResult(results);//结果数据解析 } /** * 识别回调错误 */ @Override public void onError(SpeechError error) { showTip(error.getPlainDescription(true)); } }; /** * 语音识别结果数据解析 * * @param results */ private static void parsingResult(RecognizerResult results) { //获取解析结果 String text = JsonParser.parseIatResult(results.getResultString()); String sn = null; // 读取json结果中的sn字段 try { JSONObject resultJson = new JSONObject(results.getResultString()); sn = resultJson.optString("sn"); } catch (JSONException e) { e.printStackTrace(); } mIatResults.put(sn, text); StringBuffer resultBuffer = new StringBuffer(); for (String key : mIatResults.keySet()) { resultBuffer.append(mIatResults.get(key)); } dictationResults = resultBuffer.toString();//听写结果显示 //回调 mSpeechCallback.dictationResults(dictationResults); Log.d(TAG,dictationResults); } /** * 听写参数设置 * * @return */ public static void setDictationParam() { // 清空参数 mIat.setParameter(SpeechConstant.PARAMS, null); // 设置听写引擎 mIat.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType); // 设置返回结果格式 mIat.setParameter(SpeechConstant.RESULT_TYPE, resultType); if (language.equals("zh_cn")) { String lag = mSharedPreferences.getString("iat_language_preference", "mandarin"); Log.e(TAG, "language:" + language);// 设置语言 mIat.setParameter(SpeechConstant.LANGUAGE, "zh_cn"); // 设置语言区域 mIat.setParameter(SpeechConstant.ACCENT, lag); } else { mIat.setParameter(SpeechConstant.LANGUAGE, language); } Log.e(TAG, "last language:" + mIat.getParameter(SpeechConstant.LANGUAGE)); //此处用于设置dialog中不显示错误码信息 //mIat.setParameter("view_tips_plain","false"); // 设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理 mIat.setParameter(SpeechConstant.VAD_BOS, mSharedPreferences.getString("iat_vadbos_preference", "4000")); // 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入, 自动停止录音 mIat.setParameter(SpeechConstant.VAD_EOS, mSharedPreferences.getString("iat_vadeos_preference", "1000")); // 设置标点符号,设置为"0"返回结果无标点,设置为"1"返回结果有标点 mIat.setParameter(SpeechConstant.ASR_PTT, mSharedPreferences.getString("iat_punc_preference", "1")); // 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限 mIat.setParameter(SpeechConstant.AUDIO_FORMAT, "wav"); mIat.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory() + "/msc/iat.wav"); } /** * 开始听写 */ public static void startDictation(SpeechCallback speechCallback){ mSpeechCallback = speechCallback; if( null == mIat ){ // 创建单例失败,与 21001 错误为同样原因,参考 http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=9688 showTip( "创建对象失败,请确认 libmsc.so 放置正确,且有调用 createUtility 进行初始化" ); return; } mIatResults.clear();//清除数据 setDictationParam(); // 设置参数 mIatDialog.setListener(mRecognizerDialogListener);//设置监听 mIatDialog.show();// 显示对话框 //获取字体所在的控件 TextView tvLink = Objects.requireNonNull(mIatDialog.getWindow()).getDecorView().findViewWithTag("textlink"); tvLink.setText(" "); tvLink.getPaint().setFlags(Paint.SUBPIXEL_TEXT_FLAG);//取消下划线 tvLink.setEnabled(false);//禁用点击 } /** * Toast提示 * * @param msg */ private static void showTip(String msg) { Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show(); } //语音回调 private static SpeechCallback mSpeechCallback; /** * 语音回调接口 */ public interface SpeechCallback { /** * 听写结果 */ void dictationResults(String goodsName); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南