iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >300行代码让外婆实现语音搜索购物功能
  • 127
分享到

300行代码让外婆实现语音搜索购物功能

2024-04-02 19:04:59 127人浏览 八月长安
摘要

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

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

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

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

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

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

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

效果示例

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 {
 //音频文件转写能力 SDK
 implementation '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 xxx
 keyPassWord xxxxxx
 storePassword xxxxxx
 v1SigningEnabled true
 v2SigningEnabled true
 }
 
}
 
buildTypes {
 release {
 minifyEnabled false
 proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
 }
 
 debug {
 signingConfig signingConfigs.release
 debuggable 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" />
 
<application
 android: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 permission
 int permission = ActivityCompat.checkSelfPermission(activity,
  Manifest.permission.WRITE_EXTERNAL_STORAGE);
 if (permission != PackageManager.PERMISSION_GRANTED) {
 // We don't have permission so prompt the user
 ActivityCompat.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()) {
  // 转写结果处理。
 }
 }
 @Override
 public void onError(String taskId, int errorCode, String message) {
 // 转写错误回调函数。
 }
 @Override
 public void onInitComplete(String taskId, Object ext) {
 // 预留接口。
 }
 @Override
 public void onUploadProgress(String taskId, double progress, Object ext) {
 // 预留接口。
 }
 @Override
 public void onEvent(String taskId, int eventId, Object ext) {
 // 预留接口。
 }
};

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


private MLRemoteAftListener asrListener = new MLRemoteAftListener() {
 @Override
 public void onInitComplete(String taskId, Object ext) {
 Log.e(TAG, "MLAsrCallBack onInitComplete");
 // 长语音初始化完成,开始转写
 start(taskId);
 }
 @Override
 public void onUploadProgress(String taskId, double progress, Object ext) {
 Log.e(TAG, " MLAsrCallBack onUploadProgress");
 }
 @Override
 public void onEvent(String taskId, int eventId, Object ext) {
 // 用于长语音
 Log.e(TAG, "MLAsrCallBack onEvent" + eventId);
 if (MLAftEvents.UPLOADED_EVENT == eventId) { // 文件上传成功
  // 获取转写结果
  startQueryResult(taskId);
 }
 }
 @Override
 public 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());
   }
  }
  }
 }
 }
 @Override
 public 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() {
 @Override
 public void run() {
  getResult(taskId);
 }
 };
 // 10s轮训获取长语音转写结果
 mTimer.schedule(mTimerTask, 5000, 10000);
 // 界面销毁前要清除 timerTaskMap
 timerTaskMap.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;
}

到此这篇关于300行代码让外婆实现语音搜索购物功能的文章就介绍到这了,更多相关外婆实现语音搜索购物内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 300行代码让外婆实现语音搜索购物功能

本文链接: https://www.lsjlt.com/news/122534.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 300行代码让外婆实现语音搜索购物功能
    “阿强,手写板怎么又不见了?” 最近,程序员阿强的那位勇于尝试新事物的外婆,又迷上了网购。在不太费劲儿地把购物软件摸得门儿清之后,没想到,本以为顺畅的网购之路,卡在了搜索物品上。 在...
    99+
    2024-04-02
  • Flutter实现购物车功能(代码+逻辑)
    目录一、初始化时判断是否为登录状态二、分析点击全选和非全选状态三、改变单个物品check四、点击数量增加和减少按钮改变当前显示number值五、长按删除数据一、初始化时判断是否为登录...
    99+
    2024-04-02
  • 使用Html5怎么实现一个语音搜索功能
    这篇文章给大家介绍使用Html5怎么实现一个语音搜索功能,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。html是什么html的全称为超文本标记语言,它是一种标记语言,包含了一系列标签.通过这些标签可以将网络上的文档格式...
    99+
    2023-06-09
  • Vue实现简单搜索功能的示例代码
    目录1、概述2、功能逻辑2.1功能流程2.2 流程图3、功能实现3.1 vue组件化3.2 代码3.3 动态效果1、概述 在vue项目中,搜索功能是我们经常需要使用的一个场景,最常用...
    99+
    2023-03-19
    Vue实现搜索功能 Vue搜索功能 Vue搜索
  • C#实现商城购物功能的代码怎么写
    这篇文章主要介绍“C#实现商城购物功能的代码怎么写”,在日常操作中,相信很多人在C#实现商城购物功能的代码怎么写问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C#实现商城购物功能的代码怎么写”的疑惑有所帮助!...
    99+
    2023-06-29
  • react+axios实现github搜索用户功能(示例代码)
    加载 请求成功 请求失败 在文件路径点击cmd 回车 首先把服务器打开 npm start app.js import React, { Component } ...
    99+
    2024-04-02
  • java web开发之购物车功能实现示例代码
    之前没有接触过购物车的东东,也不知道购物车应该怎么做,所以在查询了很多资料,总结一下购物车的功能实现。查询的资料,找到三种方法:用cookie实现购物车;用session实现购物车;用cookie和数据库(购物车信息持久化)实现购物车;分析...
    99+
    2023-05-30
    java web 购物车
  • 如何用vuex代码实现简单的购物车功能
    这篇“如何用vuex代码实现简单的购物车功能”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“...
    99+
    2024-04-02
  • laravel添加角色和模糊搜索功能的实现代码
    一、添加角色  1.1 角色添加显示页面路由(get) // 角色添加页面显示 Route::get('role/add', 'RoleController@addin...
    99+
    2024-04-02
  • android语音即时通讯之录音、播放功能实现代码
    在android中,实现录音与语音播放的功能算是比较简单的,但是作为参考,还是很有必要将语音相关的知识做一个简要的记录。首先,在android中,支持录音支持两种方式。主要包括:字节流模式和文件流模式。用文件流模式进行录音操作比较简单,而且...
    99+
    2023-05-30
    android 语音 通讯
  • Java 离线中文语音文字识别功能的实现代码
    目录1、pom文件如下:2、工程结构:3、语音识别工具类4、前端交互5、前端页面6、运行效果项目需要,要实现类似小爱同学的语音控制功能,并且要离线,不能花公司一分钱。第一步就是需要把...
    99+
    2024-04-02
  • 使用Vue3+Vant组件实现App搜索历史记录功能(示例代码)
    最近在开发一款新的app项目,我自己也是第一次接触app开发,经过团队的一段时间研究调查,决定使用Vue3+Vant前端组件的模式进行开发,vue2开发我们已经用过几个项目了,所以决...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作