当前位置: 首页 > news >正文

【AIGC】讯飞长录音ASR转写,使用JAVA实现科大讯飞语音服务ASR转录功能:完整指南

文章目录

  • 讯飞ASR转写API完整指南
    • 0. 引言
    • 1. 流程图
    • 2. 讯飞ASR API介绍
    • 3. API参数说明
      • 3.1 认证参数
      • 3.2 上传基本参数
      • 3.3 查询结果参数
      • 3.4 orderResult 字段
      • 3.5 Lattice 字段
      • 3.6 json_1best 字段
      • 3.7 st 字段
    • 4. java代码实现
      • 4.1 生成签名
        • 签名
      • 4.2 上传音频文件
      • 4.3 获取转写结果
      • 4.4 循环获取转写结果
      • 4.5解析转写结果
    • 5. 请求与返回示例
      • 5.1 成功返回示例
      • 5.2 异步回调
        • 1. 转写结束异步回调状态
      • 5.3注意 QPS 和 API 限流
    • 6. 文档地址
    • 7. 常见问题
    • 8. 错误码
    • 9. 结论

讯飞ASR转写API完整指南

0. 引言

在这篇博客中,我们将详细解析如何使用讯飞ASR(自动语音识别)API进行音频转写,包括上传音频、查询转写结果及解析返回数据。本文将涵盖API的参数说明,并提供完整的Python代码,确保代码能够顺利执行。

1. 流程图

2. 讯飞ASR API介绍

讯飞ASR API提供了一整套音频转写的解决方案,主要流程如下:

  1. 生成签名 - 认证请求的合法性。

  2. 上传音频 - 通过URL方式或本地文件上传音频。

  3. 查询结果 - 轮询转写结果,等待识别完成。

  4. 解析结果 - 处理返回的JSON数据,提取文本和说话人信息。

3. API参数说明

3.1 认证参数

参数名说明
appId讯飞开发者平台分配的应用ID
secret_key用于生成签名的密钥
ts时间戳,单位为秒
signa认证签名,由appId、ts和secret_key计算得出

3.2 上传基本参数

根据实际情况调优

参数名说明
fileName音频文件名称
fileSize文件大小(若使用URL方式可随意填写)
duration音频时长(单位秒,可随机填写)
language语言(cn代表中文)
audioMode上传模式(urlLink 代表通过URL上传)
audioUrl音频文件的URL(需要URL编码)

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

3.3 查询结果参数

参数名说明
orderId订单ID,用于查询转写结果
resultType返回结果类型(transfer 表示最终转写文本)

成功

{"code": "000000","descInfo": "success","content": {"orderId": "DKHJQ202209021522090215490FAAE7DD0008C","taskEstimateTime": 28000}
}

失败

{"code": "26600","descInfo": "转写业务通用错误"
}

3.4 orderResult 字段

参数名类型说明
latticeList做顺滑功能的识别结果
lattice2List未做顺滑功能的识别结果,当开启顺滑和后语规整后 orderResult 才返回 lattice2 字段(需要开通权限)
labelObject转写结果标签信息,用于补充转写结果相关信息,标记转写结果角色和声道的对应关系

3.5 Lattice 字段

参数名类型说明
json_1bestString单个 VAD 的结果的 JSON 内容

3.6 json_1best 字段

参数名类型说明
stObject单个句子的结果对象

3.7 st 字段

参数名类型说明
bgString单个句子的开始时间,单位毫秒
edString单个句子的结束时间,单位毫秒
rlString分离的角色编号,取值正整数,需开启角色分离的功能才返回对应的分离角色编号
rtList输出词语识别结果集合

4. java代码实现

4.1 生成签名

在这里插入图片描述

/*** 加签加密抽象类**/
public abstract class AbstractSignature {/*** 签名ID*/private String id;/*** 加密key*/private String key;/*** 服务url*/private String url;/*** 加密算法*/private String encryptType;/*** 待加密原始字符*/private String originSign;/*** 最终生成的签名*/protected String signa;/*** 时间戳timestamp*/private String ts;/*** 请求类型,默认get*/protected String requestMethod = "GET";/*** @param id* @param key* @param url*/public AbstractSignature(String id, String key, String url) {this.id = id;this.key = key;this.url = url;this.ts = generateTs();}/*** 可设置请求类型* @param id* @param key* @param url* @param isPost 是否为POST*/public AbstractSignature(String id, String key, String url, boolean isPost) {this.id = id;this.key = key;this.url = url;if (isPost) {this.requestMethod = "POST";}else{this.requestMethod = "GET";}this.ts = generateTs();}/*** 生成ts时间*/public String generateTs() {return String.valueOf(System.currentTimeMillis() / 1000L);}/*** 完成签名,返回完整签名** @return* @throws SignatureException*/public abstract String getSigna() throws SignatureException;public String generateOriginSign() throws SignatureException {try {URL url = new URL(this.getUrl());return "host: " + url.getHost() + "\n" +"date: " + this.getTs() + "\n" +"GET " + url.getPath() + " HTTP/1.1";} catch (MalformedURLException e) {throw new SignatureException("MalformedURLException:" + e.getMessage());}}public String getId() {return id;}public void setId(String id) {this.id = id;}public String getKey() {return key;}public void setKey(String key) {this.key = key;}public String getOriginSign() {return originSign;}public void setOriginSign(String originSign) {this.originSign = originSign;}public String getTs() {return ts;}public void setTs(String ts) {this.ts = ts;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getEncryptType() {return encryptType;}public void setEncryptType(String encryptType) {this.encryptType = encryptType;}
}
签名
public class LfasrSignature extends AbstractSignature {/**** @param appId* @param keySecret*/public LfasrSignature(String appId, String keySecret) {super(appId, keySecret, null);}@Overridepublic String getSigna() throws SignatureException {if (ObjectUtils.isEmpty(this.signa)) {this.setOriginSign(generateOriginSign());this.signa = generateSignature();}return this.signa;}/*** 生成最终的签名,需要先生成原始sign** @throws SignatureException*/public String generateSignature() throws SignatureException {return CryptTools.hmacEncrypt(CryptTools.HMAC_SHA1, this.getOriginSign(), this.getKey());}/*** 生成待加密原始字符** @throws NoSuchAlgorithmException*/@Overridepublic String generateOriginSign() throws SignatureException {return CryptTools.md5Encrypt(this.getId() + this.getTs());}
}

4.2 上传音频文件

1、文件上传
#概述
首先调用文件上传接口,上传待转写音频文件的基本信息(文件名、大小等)和相关的可配置参数。
调用成功,返回订单ID(orderId,用于查询结果或者联调排查问题时使用),是后续接口的必传参数。
#请求示例

https://raasr.xfyun.cn/v2/api/upload?duration=200&signa=Je5YsBvPcsbB4qy8Qvzd367fiv0%3D&fileName=%E9%98%B3%E5%85%89%E6%80%BB%E5%9C%A8%E9%A3%8E%E9%9B%A8%E5%90%8E.speex-wb&fileSize=11895&sysDicts=uncivilizedLanguage&appId=3e79d91c&ts=1662101767

#URL

 POST https: //raasr.xfyun.cn/v2/api/upload

#请求头

Content-Type: application/json; charset=UTF-8,Chunked: false

#signa生成

    public String createAsrUpload(String audioUrl, String language, Long orgId, String pd) {HashMap<String, Object> params = new HashMap<>(32);try {String fileName = new File(new URL(audioUrl).getPath()).getName();LfasrSignature lfasrSignature = new LfasrSignature(appid, keySecret);params.put("appId", appid);params.put("signa", lfasrSignature.getSigna());params.put("ts", lfasrSignature.getTs());params.put("fileName", fileName);params.put("fileSize", "url自定义、file实际值");params.put("duration", "200");params.put("audioMode", "urlLink");params.put("roleType", "1");params.put("roleNum", "2");// 远近场模式 1:远场模式 (默认) 2:近场模式params.put("eng_vad_mdn", "2");String encodeAudioUrl = URLEncoder.encode(audioUrl, "UTF-8");params.put("audioUrl", encodeAudioUrl);if (encodeAudioUrl.length() > 512) {throw new RuntimeException("audioUrl length must not be greater than 512");}if (StringUtils.isNotBlank(language)) {params.put("language", language);} else {params.put("language", "cn");}if (StringUtils.isNotBlank(pd)) {params.put("pd", pd);}} catch (Exception e) {throw new Exception("参数构建失败: " + e.getMessage());}// String paramString = HttpUtil.parseMapToPathParam(params);String url ="https://raasr.xfyun.cn" + "/v2/api/upload" + "?" + paramString;String response = HttpUtil.iflyrecUpload(url, null);return response;}

注意对参数的key和value编码

URLEncoder.encode(entry.getKey(), UTF8)

4.3 获取转写结果

status == 4

    private static final String API_GET_RESULT = "/v2/api/getResult";/*** 获取任务结果** @param orderId 任务id* @return 请求结果* @throws SignatureException 签名异常*/public String getResult(String orderId) throws SignatureException {HashMap<String, Object> map = new HashMap<>(16);LfasrSignature lfasrSignature = new LfasrSignature(appid, keySecret);map.put("orderId", orderId);map.put("signa", lfasrSignature.getSigna());map.put("ts", lfasrSignature.getTs());map.put("appId", appid);map.put("resultType", "transfer");String paramString = HttpUtil.parseMapToPathParam(map);String url = HOST + API_GET_RESULT + "?" + paramString;String response = HttpUtil.iflyrecGet(url);return response;}

查询结构返回数据

{"code": "000000","descInfo": "success","content": {"orderInfo": {"orderId": "DKHJQ2022090510220905100562536FEF00062","failType": 0,"status": 4,"originalDuration": 200,"realDuration": 1878},"orderResult": "{\"lattice\":[{\"json_1best\":\"{\\\"st\\\":{\\\"sc\\\":\\\"0.86\\\",\\\"pa\\\":\\\"0\\\",\\\"rt\\\":[{\\\"ws\\\":[{\\\"cw\\\":[{\\\"w\\\":\\\"\\\",\\\"wp\\\":\\\"n\\\",\\\"wc\\\":\\\"1.0000\\\"}],\\\"wb\\\":1,\\\"we\\\":16},{\\\"cw\\\":[{\\\"w\\\":\\\"\\\",\\\"wp\\\":\\\"n\\\",\\\"wc\\\":\\\"1.0000\\\"}],\\\"wb\\\":17,\\\"we\\\":36},{\\\"cw\\\":[{\\\"w\\\":\\\"\\\",\\\"wp\\\":\\\"n\\\",\\\"wc\\\":\\\"1.0000\\\"}],\\\"wb\\\":37,\\\"we\\\":52},{\\\"cw\\\":[{\\\"w\\\":\\\"\\\",\\\"wp\\\":\\\"n\\\",\\\"wc\\\":\\\"1.0000\\\"}],\\\"wb\\\":53,\\\"we\\\":80},{\\\"cw\\\":[{\\\"w\\\":\\\"测试\\\",\\\"wp\\\":\\\"n\\\",\\\"wc\\\":\\\"1.0000\\\"}],\\\"wb\\\":81,\\\"we\\\":116},{\\\"cw\\\":[{\\\"w\\\":\\\"音频\\\",\\\"wp\\\":\\\"n\\\",\\\"wc\\\":\\\"1.0000\\\"}],\\\"wb\\\":117,\\\"we\\\":172},{\\\"cw\\\":[{\\\"w\\\":\\\"\\\",\\\"wp\\\":\\\"p\\\",\\\"wc\\\":\\\"0.0000\\\"}],\\\"wb\\\":172,\\\"we\\\":172},{\\\"cw\\\":[{\\\"w\\\":\\\"\\\",\\\"wp\\\":\\\"g\\\",\\\"wc\\\":\\\"0.0000\\\"}],\\\"wb\\\":172,\\\"we\\\":172}]}],\\\"bg\\\":\\\"50\\\",\\\"rl\\\":\\\"0\\\",\\\"ed\\\":\\\"1840\\\"}}\"}],\"lattice2\":[{\"lid\":\"0\",\"end\":\"1840\",\"begin\":\"50\",\"json_1best\":{\"st\":{\"sc\":\"0.86\",\"pa\":\"0\",\"rt\":[{\"nb\":\"1\",\"nc\":\"1.0\",\"ws\":[{\"cw\":[{\"w\":\"\",\"wp\":\"n\",\"wc\":\"1.0000\"}],\"wb\":1,\"we\":16},{\"cw\":[{\"w\":\"\",\"wp\":\"n\",\"wc\":\"1.0000\"}],\"wb\":17,\"we\":36},{\"cw\":[{\"w\":\"\",\"wp\":\"n\",\"wc\":\"1.0000\"}],\"wb\":37,\"we\":52},{\"cw\":[{\"w\":\"\",\"wp\":\"n\",\"wc\":\"1.0000\"}],\"wb\":53,\"we\":80},{\"cw\":[{\"w\":\"测试\",\"wp\":\"n\",\"wc\":\"1.0000\"}],\"wb\":81,\"we\":116},{\"cw\":[{\"w\":\"音频\",\"wp\":\"n\",\"wc\":\"1.0000\"}],\"wb\":117,\"we\":172},{\"cw\":[{\"w\":\"\",\"wp\":\"p\",\"wc\":\"0.0000\"}],\"wb\":172,\"we\":172},{\"cw\":[{\"w\":\"\",\"wp\":\"g\",\"wc\":\"0.0000\"}],\"wb\":172,\"we\":172}]}],\"pt\":\"reserved\",\"bg\":\"50\",\"si\":\"0\",\"rl\":\"0\",\"ed\":\"1840\"}},\"spk\":\"段落-0\"}]}","taskEstimateTime": 0}

4.4 循环获取转写结果

  LfasrResponse uploadResponse = lfasrClient.uploadUrl(AUDIO_URL);if (uploadResponse == null) {logger.error("上传失败,响应为空");return;}if (!StringUtils.equals(uploadResponse.getCode(), "000000")) {logger.error("上传失败,错误码:{},错误信息:{}", uploadResponse.getCode(), uploadResponse.getDescInfo());return;}String orderId = uploadResponse.getContent().getOrderId();logger.info("转写任务orderId:{}", orderId);// 3、查询转写结果int status = LfasrOrderStatusEnum.CREATED.getKey();// 循环直到订单完成或失败while (status != LfasrOrderStatusEnum.COMPLETED.getKey() && status != LfasrOrderStatusEnum.FAILED.getKey()) {LfasrResponse resultResponse = lfasrClient.getResult(orderId, TASK_TYPE);if (!StringUtils.equals(resultResponse.getCode(), "000000")) {logger.error("转写任务失败,错误码:{},错误信息:{}", resultResponse.getCode(), resultResponse.getDescInfo());return;}// 获取订单状态信息if (resultResponse.getContent() != null && resultResponse.getContent().getOrderInfo() != null) {status = resultResponse.getContent().getOrderInfo().getStatus();int failType = resultResponse.getContent().getOrderInfo().getFailType();// 根据状态输出日志LfasrOrderStatusEnum statusEnum = LfasrOrderStatusEnum.getEnum(status);if (statusEnum != null) {logger.info("订单状态:{}", statusEnum.getValue());// 如果订单失败,输出失败原因if (statusEnum == LfasrOrderStatusEnum.FAILED) {LfasrFailTypeEnum failTypeEnum = LfasrFailTypeEnum.getEnum(failType);logger.error("订单处理失败,失败原因:{}", failTypeEnum.getValue());return;}// 如果订单已完成,输出结果if (statusEnum == LfasrOrderStatusEnum.COMPLETED) {printResult(resultResponse);return;}} else {logger.error("未知的订单状态:{}", status);}} else {logger.error("返回结果中缺少订单信息");}TimeUnit.SECONDS.sleep(20);}}

4.5解析转写结果

    /*** 从转写结果的lattice数组中提取文本*/private static String getLatticeText(List<LfasrOrderResult.Lattice> latticeList) {StringBuilder resultText = new StringBuilder();for (LfasrOrderResult.Lattice lattice : latticeList) {LfasrOrderResult.Json1Best json1Best = lattice.getJson1Best();if (json1Best == null || json1Best.getSt() == null || json1Best.getSt().getRt() == null) {continue;}String rl = json1Best.getSt().getRl();StringBuilder rlText = getRlText(json1Best);resultText.append("角色-").append(rl).append(":").append(rlText).append("\n");}return resultText.toString();}
    /*** 从Json1Best中提取识别结果文本并拼接*/private static StringBuilder getRlText(LfasrOrderResult.Json1Best json1Best) {StringBuilder rlText = new StringBuilder();for (LfasrOrderResult.RecognitionResult rt : json1Best.getSt().getRt()) {if (rt.getWs() == null) {continue;}for (LfasrOrderResult.WordResult ws : rt.getWs()) {if (ws.getCw() != null && !ws.getCw().isEmpty()) {// 获取每个词的识别结果String word = ws.getCw().get(0).getW();if (word != null && !word.isEmpty()) {rlText.append(word);}}}}return rlText;}
def parse_result(self, result_json):"""解析转写结果,按说话人分组"""try:result = json.loads(result_json)speakers = {}if 'lattice2' in result:for item in result['lattice2']:speaker = item.get('spk', '未知')json_1best = json.loads(item['json_1best'])text = "".join(cw['w'] for rt in json_1best.get('st', {}).get('rt', []) for ws in rt.get('ws', []) for cw in ws.get('cw', []) if 'w' in cw)speakers.setdefault(speaker, []).append(text)return speakersexcept Exception as e:return None

5. 请求与返回示例

5.1 成功返回示例

参数回看:3.3 查询结果参数

{"code": "000000","descInfo": "success","content": {"orderId": "DKHJQ202209021522090215490FAAE7DD0008C","taskEstimateTime": 28000}
}

5.2 异步回调

1. 转写结束异步回调状态

当订单转写流程结束时会回调用户(如果录音文件转写接口 upload 传了callbackUrl),会把订单号和订单状态返回,具体的格式和参数说明如下: 回调地址示例:

GET http://ip:prot/server/xxx?orderId=DKHJQ202004291620042916580FBC96690001F&status=1

在这里插入图片描述

5.3注意 QPS 和 API 限流

  //{"code":"100012","descInfo":"access too fast, please wait a moment"}// {"code":“26603","descInfo":"接口访问频率受限”}else if (code.equals("100012") || code.equals("26603") || code.equals("26605")) {if (tryCount < MAX_RETRY_COUNT) {// 队列处理this.submitToQue(orderId, tryCount + 1, taskId ,type);} else {throw new Exception("Max retry count reached for orderId: "+orderId);}} else {throw new Exception("讯飞响应异常:" + code + "resp:" + respResult);}
CODE: 100012 26603

可进行队列延迟重试处理

6. 文档地址

讯飞ASR文档
在这里插入图片描述

7. 常见问题

录音文件转写支持哪些音频格式?
答:目前录音文件转写支持的音频格式为:已录制音频(5小时内),wav,flac,opus,m4a,mp3,单声道&多声道,支持语种:中文普通话、英语、开通的小语种以及中文方言,采样率:8KHz,16KHz

#录音文件转写支不支持并发?
答:支持,要保证同一个appid每秒请求接口次数最大值在20次以下。

#录音文件转写可以试用吗?
答:可以领取新用户礼包,根据您认证的程度,提供最多50小时的免费时长,有效期为一年。

#录音文件转写支持什么语言?
答:支持语种:中文普通话、英语,小语种以及中文方言可以到控制台-语音转写-方言/语种处添加试用或购买;设置方式参考上述语言参数切换即可

#录音文件转写的套餐扣费顺序是怎样的?
答:扣量优先级:免费试用>批量购买,即在“批量购买”的套餐额度剩余的情况下,又领取了免费试用的体验包,则领取的免费试用体验包立即生效,并被设定为当前扣量套餐。而之前购买的套餐包的额度和到期日不变。

8. 错误码

错误码 描述
26600 转写业务通用错误,检查下请求参数是否正确
26601 非法应用信息,检查下上传的appi是否正确
26602 任务ID不存在
26603 接口访问频率受限
26604 获取结果次数超过限制
26605 任务正在处理中,请稍后重试
26606 空音频,请检查
26607 转写语种未授权或已过有效期
26610 请求参数错误
26621 预处理文件大小受限(500M)
26622 预处理音频时长受限(5小时)
26623 预处理音频格式受限
26625 预处理服务时长不足。您剩余的可用服务时长不足,请移步产品页http://www.xfyun.cn/services/lfasr 进行购买或者免费领取
26631 音频文件大小受限(500M)
26632 音频时长受限(5小时)
26633 音频服务时长不足。您剩余的可用服务时长不足,请移步产品页http://www.xfyun.cn/services/lfasr 进行购买或者免费领取
26634 文件下载失败
26635 文件长度校验失败
26640 文件上传失败
26641 上传分片超过限制
26642 分片合并失败
26643 计算音频时长失败,请检查您的音频是否加密或者损坏
26650 音频格式转换失败,请检查您的音频是否加密或者损坏
26660 计费计量失败
26670 转写结果集解析失败
26671 下载转写结果失败
26680 引擎错误
26681 引擎获取订单异常
26682 引擎订单处理中
26689 引擎网络异常

9. 结论

本文详细讲解了讯飞ASR API的使用流程,包括如何生成签名、上传音频、查询结果并解析返回数据。希望这篇文章对你有所帮助!如果对你有帮助,帮忙给个一键三连,求求了,各位吴彦祖,刘亦菲们

http://www.lryc.cn/news/583560.html

相关文章:

  • JavaScript基础篇——第五章 对象(最终篇)
  • NLP革命二十年:从规则驱动到深度学习的跃迁
  • LLaMA-Omni 深度解析:打开通往无缝人机语音交互的大门
  • pip install av安装av库失败解决方法
  • Celery Django配置
  • 存储服务一NFS文件存储概述
  • Mysql基于belog恢复数据
  • 精准医疗,AR 锚定球囊扩张导管为健康护航​
  • 基于 Spark MLlib 的推荐系统实现
  • 打破传统,开启 AR 智慧课堂​
  • langchain从入门到精通(四十一)——基于ReACT架构的Agent智能体设计与实现
  • 基于BRPC构建高性能HTTP/2服务实战指南
  • 前端业务监控系统,异常上报业务,异常队列收集,异常捕获
  • 【实习篇】之Http头部字段之Disposition介绍
  • HTML + CSS + JavaScript
  • http get和http post的区别
  • C++ 中最短路算法的详细介绍
  • JAVA策略模式demo【设计模式系列】
  • LaCo: Large Language Model Pruning via Layer Collapse
  • Java 大视界 -- 基于 Java 的大数据分布式计算在生物信息学蛋白质 - 蛋白质相互作用预测中的应用(340)
  • windows指定某node及npm版本下载
  • Using Spring for Apache Pulsar:Message Production
  • Softmax函数的学习
  • 矩阵之方阵与行列式的关系
  • Flink-1.19.0源码详解6-JobGraph生成-后篇
  • Android Soundtrigger唤醒相关时序学习梳理
  • 常见 HTTP 方法的成功状态码200,204,202,201
  • C++并发编程-11. C++ 原子操作和内存模型
  • Token 和 Embedding的关系
  • 通过Tcl脚本命令:set_param labtools.auto_update_hardware 0