程序员用300行代码,让外婆实现语音搜索购物

 “阿强,手写板怎么又不见了?”

最近,程序员阿强的那位勇于尝试新事物的外婆,又迷上了网购。在不太费劲儿地把购物软件摸得门儿清之后,没想到,本以为顺畅的网购之路,卡在了搜索物品上。

在手写输入环节,要么误操作,无意中更换到不熟悉的输入法;要么误按了界面上抽象的指令字符……于是阿强也经常收到外婆发来的求助。

其实,不止是购物应用,时下智能手机里装载的大部APP,都是倾斜于年轻群体的交互设计,老年人想要体验学会使用,很难真香。

在一次次耐心指导外婆完成操作后,阿强,这个成熟coder给自己提了个需求:提升外婆的网购体验。不是一味让她适应输入法,而是让输入法迎合外婆的使用偏好习惯。

手动输入易出错,那就写个语音转文字的输入方法,只要启动录音按钮,实时语音识别输入,简单又快捷,外婆用了说直说好!

效果示例

3

 

应用场景

实时语音识别和音频转文字有着丰富的应用的场景。

  1. 游戏应用中的运用:当你在联机游戏场组队开黑时,通过实时语音识别跟队友无阻沟通,不占用双手的同时,也避免了开麦露出声音的尴尬。
  2. 办公应用中的运用:职场里,耗时长的会议,手打码字记录即低效,还容易漏掉细节,凭借音频文件转文字功能,转写会议讨论内容,会后对转写的文字进行梳理润色,事半功倍。
  3. 学习应用中的运用:时下越来越多的音频教学材料,一边观看一边暂停做笔记,很容易打断学习节奏,破坏学习过程的完整性,有了音频文件转写,系统的学习完教材后,再对文字进行复习梳理,学习体验更佳。

实现原理

华为机器学习服务提供实时语音识别音频文件转写能力。

实时语音识别

支持将实时输入的短语音(时长不超过60秒)转换为文本,识别准确率可达95%以上。目前支持中文普通话、英语、中英混说、法语、德语、西班牙语、意大利语、阿拉伯语的识别。

  • 支持实时出字。
  • 提供拾音界面、无拾音界面两种方式。
  • 支持端点检测,可准确定位开始和结束点。
  • 支持静音检测,语音中未说话部分不发送语音包。
  • 支持数字格式的智能转换,例如语音输入“二零二一年”时,能够智能识别为“2021年”。

音频文件转写

可将5小时内的音频文件转换成文字,支持输出标点符号,形成断句合理、易于理解的文本信息。同时支持生成带有时间戳的文本信息,便于后续进行更多功能开发。当前版本支持中英文的转写。

开发步骤

开发前准备

1. 配置华为Maven仓地址并将agconnect-services.json文件放到app目录下:
打开Android Studio项目级“build.gradle”文件。


添加HUAWEI agcp插件以及Maven代码库。

  • 在“allprojects > repositories”中配置HMS Core SDK的Maven仓地址。
  • 在“buildscript > repositories”中配置HMS Core SDK的Maven仓地址。
  • 如果App中添加了“agconnect-services.json”文件则需要在“buildscript > dependencies”中增加agcp配置。
buildscript {repositories {google()jcenter()maven { url 'https://developer.huawei.com/repo/' }}dependencies {classpath 'com.android.tools.build:gradle:3.5.4'classpath 'com.huawei.agconnect:agcp:1.4.1.300'// NOTE: Do not place your application dependencies here; they belong// in the individual module build.gradle files}
}allprojects {repositories {google()jcenter()maven { url 'https://developer.huawei.com/repo/' }}
}

参见云端鉴权信息使用须知,设置应用的鉴权信息。

2. 添加编译SDK依赖:

dependencies {//音频文件转写能力 SDKimplementation 'com.huawei.hms:ml-computer-voice-aft:2.2.0.300'// 实时语音转写 SDK.implementation 'com.huawei.hms:ml-computer-voice-asr:2.2.0.300'// 实时语音转写 plugin.implementation 'com.huawei.hms:ml-computer-voice-asr-plugin:2.2.0.300'...
}
apply plugin: 'com.huawei.agconnect'  // HUAWEI agconnect Gradle plugin

3.在app的build中配置签名文件并将签名文件(xxx.jks)放入app目录下:

signingConfigs {release {storeFile file("xxx.jks")keyAlias xxxkeyPassword xxxxxxstorePassword xxxxxxv1SigningEnabled truev2SigningEnabled true}}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}debug {signingConfig signingConfigs.releasedebuggable true}
}

4.在Manifest.xml中添加权限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" /><applicationandroid:requestLegacyExternalStorage="true"...
</application>

接入实时语音识别能力

1.进行权限动态申请:

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {requestCameraPermission();
}private void requestCameraPermission() {final String[] permissions = new String[]{Manifest.permission.RECORD_AUDIO};if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) {ActivityCompat.requestPermissions(this, permissions, Constants.AUDIO_PERMISSION_CODE);return;}
}

2.创建Intent,用于设置实时语音识别参数。

//设置您应用的鉴权信息
MLApplication.getInstance().setApiKey(AGConnectServicesConfig.fromContext(this).getString("client/api_key"));通过intent进行识别设置。
Intent intentPlugin = new Intent(this, MLAsrCaptureActivity.class)// 设置识别语言为英语,若不设置,则默认识别英语。支持设置:"zh-CN":中文;"en-US":英语等。.putExtra(MLAsrCaptureConstants.LANGUAGE, MLAsrConstants.LAN_ZH_CN)// 设置拾音界面是否显示识别结果.putExtra(MLAsrCaptureConstants.FEATURE, MLAsrCaptureConstants.FEATURE_WORDFLUX);
startActivityForResult(intentPlugin, "1");

3.覆写“onActivityResult”方法,用于处理语音识别服务返回结果。

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {super.onActivityResult(requestCode, resultCode, data);String text = "";if (null == data) {addTagItem("Intent data is null.", true);}if (requestCode == "1") {if (data == null) {return;}Bundle bundle = data.getExtras();if (bundle == null) {return;}switch (resultCode) {case MLAsrCaptureConstants.ASR_SUCCESS:// 获取语音识别得到的文本信息。if (bundle.containsKey(MLAsrCaptureConstants.ASR_RESULT)) {text = bundle.getString(MLAsrCaptureConstants.ASR_RESULT);}if (text == null || "".equals(text)) {text = "Result is null.";Log.e(TAG, text);} else {//将语音识别结果设置在搜索框上searchEdit.setText(text);goSearch(text, true);}break;// 返回值为MLAsrCaptureConstants.ASR_FAILURE表示识别失败。case MLAsrCaptureConstants.ASR_FAILURE:// 判断是否包含错误码。if (bundle.containsKey(MLAsrCaptureConstants.ASR_ERROR_CODE)) {text = text + bundle.getInt(MLAsrCaptureConstants.ASR_ERROR_CODE);// 对错误码进行处理。}// 判断是否包含错误信息。if (bundle.containsKey(MLAsrCaptureConstants.ASR_ERROR_MESSAGE)) {String errorMsg = bundle.getString(MLAsrCaptureConstants.ASR_ERROR_MESSAGE);// 对错误信息进行处理。if (errorMsg != null && !"".equals(errorMsg)) {text = "[" + text + "]" + errorMsg;}}//判断是否包含子错误码。if (bundle.containsKey(MLAsrCaptureConstants.ASR_SUB_ERROR_CODE)) {int subErrorCode = bundle.getInt(MLAsrCaptureConstants.ASR_SUB_ERROR_CODE);// 对子错误码进行处理。text = "[" + text + "]" + subErrorCode;}Log.e(TAG, text);break;default:break;}}
}

接入音频文件转写能力

1.申请动态权限。

private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static final String[] PERMISSIONS_STORAGE = {Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE };
public static void verifyStoragePermissions(Activity activity) {// Check if we have write permissionint permission = ActivityCompat.checkSelfPermission(activity,Manifest.permission.WRITE_EXTERNAL_STORAGE);if (permission != PackageManager.PERMISSION_GRANTED) {// We don't have permission so prompt the userActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE,REQUEST_EXTERNAL_STORAGE);}
}

2.新建音频文件转写引擎并初始化;新建音频文件转写配置器。

// 设置 ApiKey.
MLApplication.getInstance().setApiKey(AGConnectServicesConfig.fromContext(getApplication()).getString("client/api_key"));
MLRemoteAftSetting setting = new MLRemoteAftSetting.Factory()// 设置转写语言编码,使用BCP-47规范,当前支持中文普通话、英文转写。.setLanguageCode("zh")// 设置是否在转写输出的文本中自动增加标点符号,默认为false。.enablePunctuation(true)// 设置是否连带输出每段音频的文字转写结果和对应的音频时移,默认为false(此参数仅小于1分钟的音频需要设置)。.enableWordTimeOffset(true)// 设置是否输出句子出现在音频文件中的时间偏移值,默认为false。.enableSentenceTimeOffset(true).create();// 新建音频文件转写引擎。
MLRemoteAftEngine engine = MLRemoteAftEngine.getInstance();
engine.init(this);
// 将侦听器回调传给第一步中定义的音频文件转写引擎中
engine.setAftListener(aftListener);

3.新建侦听器回调,用于处理音频文件转写结果:

短语音转写:适用于时长小于1分钟的音频文件

private MLRemoteAftListener aftListener = new MLRemoteAftListener() {public void onResult(String taskId, MLRemoteAftResult result, Object ext) {// 获取转写结果通知。if (result.isComplete()) {// 转写结果处理。}}@Overridepublic void onError(String taskId, int errorCode, String message) {// 转写错误回调函数。}@Overridepublic void onInitComplete(String taskId, Object ext) {// 预留接口。}@Overridepublic void onUploadProgress(String taskId, double progress, Object ext) {// 预留接口。}@Overridepublic void onEvent(String taskId, int eventId, Object ext) {// 预留接口。}
};

长语音转写:适用于时长大于1分钟的音频文件

private MLRemoteAftListener asrListener = new MLRemoteAftListener() {@Overridepublic void onInitComplete(String taskId, Object ext) {Log.e(TAG, "MLAsrCallBack onInitComplete");// 长语音初始化完成,开始转写start(taskId);}@Overridepublic void onUploadProgress(String taskId, double progress, Object ext) {Log.e(TAG, " MLAsrCallBack onUploadProgress");}@Overridepublic void onEvent(String taskId, int eventId, Object ext) {// 用于长语音Log.e(TAG, "MLAsrCallBack onEvent" + eventId);if (MLAftEvents.UPLOADED_EVENT == eventId) { // 文件上传成功// 获取转写结果startQueryResult(taskId);}}@Overridepublic void onResult(String taskId, MLRemoteAftResult result, Object ext) {Log.e(TAG, "MLAsrCallBack onResult taskId is :" + taskId + " ");if (result != null) {Log.e(TAG, "MLAsrCallBack onResult isComplete: " + result.isComplete());if (result.isComplete()) {TimerTask timerTask = timerTaskMap.get(taskId);if (null != timerTask) {timerTask.cancel();timerTaskMap.remove(taskId);}if (result.getText() != null) {Log.e(TAG, taskId + " MLAsrCallBack onResult result is : " + result.getText());tvText.setText(result.getText());}List<MLRemoteAftResult.Segment> words = result.getWords();if (words != null && words.size() != 0) {for (MLRemoteAftResult.Segment word : words) {Log.e(TAG, "MLAsrCallBack word  text is : " + word.getText() + ", startTime is : " + word.getStartTime() + ". endTime is : " + word.getEndTime());}}List<MLRemoteAftResult.Segment> sentences = result.getSentences();if (sentences != null && sentences.size() != 0) {for (MLRemoteAftResult.Segment sentence : sentences) {Log.e(TAG, "MLAsrCallBack sentence  text is : " + sentence.getText() + ", startTime is : " + sentence.getStartTime() + ". endTime is : " + sentence.getEndTime());}}}}}@Overridepublic void onError(String taskId, int errorCode, String message) {Log.i(TAG, "MLAsrCallBack onError : " + message + "errorCode, " + errorCode);switch (errorCode) {case MLAftErrors.ERR_AUDIO_FILE_NOTSUPPORTED:break;}}
};
// 上传转写任务
private void start(String taskId) {Log.e(TAG, "start");engine.setAftListener(asrListener);engine.startTask(taskId);
}
// 获取转写结果
private Map<String, TimerTask> timerTaskMap = new HashMap<>();
private void startQueryResult(final String taskId) {Timer mTimer = new Timer();TimerTask mTimerTask = new TimerTask() {@Overridepublic void run() {getResult(taskId);}};// 10s轮训获取长语音转写结果mTimer.schedule(mTimerTask, 5000, 10000);// 界面销毁前要清除 timerTaskMaptimerTaskMap.put(taskId, mTimerTask);
}

4.获取音频,上传音频文件到转写引擎中:

//获取音频文件的uri
Uri uri = getFileUri();
//获取音频时间
Long audioTime = getAudioFileTimeFromUri(uri);
//判断音频时间是否超过60秒
if (audioTime < 60000) {// uri为从本地存储或者录音机读取到的语音资源,仅支持时长在1分钟之内的本地音频this.taskId = this.engine.shortRecognize(uri, this.setting);Log.i(TAG, "Short audio transcription.");
} else {// longRecognize为长语音转写接口,用于转写时长大于1分钟,小于5小时的语音。this.taskId = this.engine.longRecognize(uri, this.setting);Log.i(TAG, "Long audio transcription.");
}private Long getAudioFileTimeFromUri(Uri uri) {Long time = null;Cursor cursor = this.getContentResolver().query(uri, null, null, null, null);if (cursor != null) {cursor.moveToFirst();time = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION));} else {MediaPlayer mediaPlayer = new MediaPlayer();try {mediaPlayer.setDataSource(String.valueOf(uri));mediaPlayer.prepare();} catch (IOException e) {Log.e(TAG, "Failed to read the file time.");}time = Long.valueOf(mediaPlayer.getDuration());}return time;
}

 

 

>>访问华为开发者联盟官网,了解更多相关内容

>>获取开发指导文档

>>华为移动服务开源仓库地址:GitHub、Gitee

关注我们,第一时间了解华为移动服务最新技术资讯~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/56856.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android App开发语音处理之系统自带的语音引擎、文字转语音、语音识别的讲解及实战(超详细 附源码)

需要源码请点赞关注收藏后评论区留下QQ~~~ 一、系统自带的语音引擎 语音播报的本质是将书面文字转换成自然语言的音频流&#xff0c;这个转换操作被称作语音合成&#xff0c;又称TTS&#xff08;从文本到语音&#xff09;在转换过程中&#xff0c;为了避免机械合成的呆板和停顿…

自动聊天挂机项目

三木智能聊天云控脚本

拼多多顶级佣金助手-微信群自动发单

注意&#xff1a;微信版本必须使用软件根目录下的安装包安装 1.进入多多进宝网站-注册多多进宝-并创建推广位... 2.打开软件点击-多多授权-授权页面-授权成功会跳转百度,复制百度地址栏code后面的code码... 3.粘贴到软件CODE输入框-点击-确定授权... 4.推广位ID框中出现你自…

30行python代码实现微信自动陪女盆友聊天(itchat-uos + 无限制调用)

先放代码&#xff1a; 再放个文字的&#xff08;方便复制&#xff09;&#xff1a; import itchat import requestsdef get_reply(keyword):try:url f"https://open.drea.cc/bbsapi/chat/get?keyWord{keyword}&userNametype%3Dbbs"res requests.get(url)data…

[做初中数学题做到打起来了]跟同事为了他小孩的数学题杠上了

✅作者简介&#xff1a;人工智能专业本科在读&#xff0c;喜欢计算机与编程&#xff0c;写博客记录自己的学习历程。 &#x1f34e;个人主页&#xff1a;小嗷犬的博客 &#x1f34a;个人信条&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;…

Whatsapp有效号码筛选 – 号码过滤器 | 电话号码过滤筛选 |配合 Whatsapp营销协议群发软件

Whatsapp号码过滤器能进行快速电话号码过滤筛选&#xff0c;过滤已开通whatsapp号码有效性&#xff0c;精准筛选出有用的whatsapp号码。Whatsapp号码筛选器可与各类Whatsapp群发软件、电话号码采集软件搭配使用&#xff0c;有助于辨识有效Whatsapp号码&#xff0c;过滤无效号码…

网络流量pcap包特征提取并保存

前言 新手写博客&#xff0c;写博客主要目的是为了记录自己做项目的一些过程。关于网络流量项目&#xff0c;刚开始做项目一个月左右&#xff0c;所写的博客必然有很多不足的地方&#xff0c;欢迎大家交流和指教。 获取pcap包 pcap包特征提取第一步是要获得pcap包。pcap一般…

TCP、UDP数据包大小的限制

一、概述 首先要看TCP/IP协议&#xff0c;涉及到四层&#xff1a;链路层&#xff0c;网络层&#xff0c;传输层&#xff0c;应用层。    其中以太网&#xff08;Ethernet&#xff09;的数据帧在链路层    IP包在网络层    TCP或UDP包在传输层    TCP或UDP中的数据&…

为程序员准备的英语学习资料

程序员学习英语有用吗&#xff1f;当然有用&#xff0c;而且有很大用&#xff01; 当我们浏览StackOverFlow的时候&#xff0c;当我们和外国技术大牛交流的时候&#xff0c;当我们去面试FLAG&#xff08;Facebook&#xff0c;LinkedIn&#xff0c;Amazon&#xff0c;Google&…

黑马程序员python培训PDF下载

在分享资源之前&#xff0c;大家可以先想清楚未来的职业发展方向。毕竟只有找到未来职业发展方向&#xff0c;才更清楚当下重点学习的内容。 根据上图我们基本上一目了然&#xff0c;找到自己未来要发展的方向。在找到之后可以在各个招聘app上了解一下相关岗位的技能要求是什么…

黑马程序员7

算数运算符重载 运算符重载概念&#xff1a;对已有的运算符重新进行定义&#xff0c;赋予其另一种功能&#xff0c;以适应不同的数据类型 加号运算符 通过自己写函数&#xff0c;实现两个对象相加属性后返回新的对象 两种方式重载 成员函数方式重载 全局函数重载 上来 perso…

黑马程序员14套经典IT教程+面试宝典

很多同学对互联网比较感兴趣 &#xff0c;奈何苦恼不知道如何入门。今天免费给大家分享一波&#xff0c;黑马程序员14套经典IT教程程序员面试宝典&#xff01;涉及Java、前端、Python、大数据、软件测试、UI设计、新媒体短视频等。从厌学到学嗨&#xff0c;你只差一套黑马教程&…

latex添加参考文献(I found no \citation commands、I found no \bibdata command、I found no \bibstyle comma)

1、处理模板里的reference.bib文件 2、使用reference.bib文件 3、添加引用 4、配置编译器&#xff0c;否则编译会报错。在该目录填写如下代码&#xff1a; {"latex-workshop.latex.outDir": "%DIR%/.build","latex-workshop.view.pdf.viewer": &…

八股文总是忘?一张图牢记JVM内存结构|金三银四系列

金三银四又来啦&#xff01;八股文还是得复习起来&#xff0c;最近准备把一些常见的八股文知识点聊聊。 本文详解了JVM内存结构和各个部分详细内容&#xff0c;应付面试绰绰有余&#xff01; 点击上方“后端开发技术”&#xff0c;选择“设为星标” &#xff0c;优质资源及时送…

【Android面试】2023最新面试专题:Java虚拟机原理(一)

1 描述JVM类加载过程 这道题想考察什么&#xff1f; 了解JVM是如何加载类的&#xff0c;并且通过JVM类加载过程能更直观了解掌握如APT注解处理器执行、热修复等技术的本质 考察的知识点 JVM类加载过程 考生如何回答 类加载的本质 一般情况下&#xff0c;类的数据都是在C…

JVM 锁优化和逃逸分析详解

1 锁优化 JVM 在加锁的过程中&#xff0c;会采用自旋、自适应、锁消除、锁粗化等优化手段来提升代码执行效率。 1.1 自旋锁和自适应自旋 现在大多的处理器都是多核处理器 &#xff0c;如果在多核心处理器&#xff0c;有让两个或者以上的线程并行执行&#xff0c;我们可以让一个…

kubernetes学习之路--BadPods(Part1)

摘要&#xff1a;对Pod配置进行实战学习&#xff0c;以BadPods项目为例学习危险配置。 目录 一.BadPods介绍及使用 二.BadPods配置学习 2.1 less1--Everything allowed 基本操作学习 2.2 less1--Everything allowed 渗透学习 一.BadPods介绍及使用 项目地址&#xff1a;h…

基于GPT3.5实现本地知识库解决方案-利用向量数据库和GPT向量接口-实现智能回复并限制ChatGPT回答的范围...

标题有点长&#xff0c;但是基本也说明出了这篇文章的主旨&#xff0c;那就是利用GPT AI智能回答自己设置好的问题 既能实现自己的AI知识库机器人&#xff0c;又能节省ChatGPT调用的token成本费用。 代码仓库地址 document.ai: 基于GPT3.5的通用本地知识库解决方案 下面图片是整…

英语语法大全

文章目录 一、主语1、名词、代词和动词做主语2、主语从句做主语&#xff0c;谓语动词用单数3、主语从句练习 二、谓语动词1、谓语动词种类2、主谓一致 三、宾语1、单宾语2、双宾语3、复合宾语4、宾语从句 四、定语1、定语从句2、定语从句的翻译 五、状语1、分词做状语2、独立主…

规模效应和网络效应

前几天小米造车、华为造车、大疆造车、滴滴造车、百度造车集中PR。群中讨论智能新能源汽车的未来集中。甚至延伸讨论到了&#xff1a;云计算的未来集中度、SaaS的未来集中度。怎么能提高行业集中度&#xff0c;是我们都在苦苦思考的。 于是就讨论到两个词&#xff1a;规模效应、…