版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_42228694/article/details/103289916

智能语音交互

智能语音交互(Intelligent Speech Interaction),是基于语音识别、语音合成、自然语言理解等技术,为企业在多种实际应用场景下,赋予产品“能听、会说、懂你”式的智能人机交互体验.适用于多个应用场景中,包括智能问答、智能质检、法庭庭审实时记录、实时演讲字幕、访谈录音转写等场景,在金融、保险、司法、电商等多个领域均有应用案例

一.语音术语

1.采样率(sample rate)

音频采样率是指录音设备在一秒钟内对声音信号的采样次数,采样频率越高声音的还原就越真实越自然

目前语音识别服务只支持16000Hz和8000Hz两种采样率,其中8000Hz一般是电话业务使用,其余都使用16000Hz

调用语音识别服务时需要设置采样率参数.参数数值,语音数据和项目配置三者必须一致,否则识别效果会非常差

2.采样位数(sample size)

即采样值或取样值(就是将采样样本幅度量化).它是用来衡量声音波动变化的一个参数,也可以说是声卡的分辨率.它的数值越大,分辨率也就越高,所发出声音的能力越强

每个采样数据记录的是振幅, 采样精度取决于采样位数的大小:

  • 1 字节 最低标准

  • 2 字节 CD标准

  • 4 字节 无特殊标准不必要

3.语音编码(format)

语音编码指语音数据存储和传输的方式,语音编码和语音文件格式不同.例如常见的.WAV文件格式,会在其头部定义语音数据的具体编码,其中的音频数据通常是使用PCM编码,但也有可能是AMR或其他编码

4.声道(sound channel)

声道是指声音在录制时在不同空间位置采集的相互独立的音频信号,所以声道数也就是声音录制时的音源数量.常见的音频数据为单声道或双声道(立体声)

除录音文件识别以外的服务只支持单声道(mono)语音数据,如果数据是双声道或其他,需要先转换为单声道才能识别

5.逆文本规整(ITN)

逆文本规整(inverse text normalization)是指语音转换为文本时使用标准化的格式来展示数字、金额、日期和地址等对象,以便符合通常的阅读习惯.以下是一些例子:

语音原始文本开启ITN的识别结果
百分之二十20%
一千六百八十元1680元
五月十一号5月11号
请拨幺幺零请拨110

二.服务相关概念

1.项目标识(Appkey)

您可以在智能语音管控台中创建多个项目,每个项目有一个唯一标识,就是Appkey

服务通过Appkey获得项目的具体配置信息

2.访问标识(access key)

访问标识是您的程序访问API的凭证,能提供此凭证的程序具有您账户完全的权限.访问标识由id和secret两部分组成:Access key ID 是类似身份的标识,而 access key secret 的作用是签名访问参数,以防被篡改.两者必须组合使用,其中Access key secret 类似登录密码

3.中间结果(intermediate result)

在调用语音识别服务时可以设置是否返回中间结果

  • 设置为false时只在语音全部识别完后返回一次完整的结果

  • 设置为true时除了最后一次完整的结果之外,还会在您说话的同时返回中间结果

  • 中间结果可能在后续返回结果中被修正

  • 每次中间结果增量返回的字数并不固定

4.任务标识(task_id)

每一个语音服务请求都会有一个唯一的task_id,由SDK自动生成,可用于定位问题

5.访问令牌(access token)

访问令牌(Access Token)是调用智能语音服务的凭证.您可以使用阿里云公共SDK调用云端服务获取Access Token.调用时需要提供您阿里云账号的AccessKey ID和AccessKey Secret

访问令牌使用前需要通过ExpireTime参数获取有效期时间戳,*过期则需要重新获取.

在管控台点击 总览 > 获取AccessToken,可获取用于测试的Token

调用云端服务的返回示例如下:

{"NlsRequestId": "aed8c1af075347819118ff6bf8111168","RequestId": "0989F63E-5069-4AB0-822B-5BD2D95356DF","Token": {"ExpireTime": 1527592757,"Id": "124fc7526f434b8c8198d6196b0a1c8e","UserId": "123456789012"}
}
  • Token->Id 为本次分配的访问令牌Access token

  • Token->ExpireTime 为此令牌的有效期时间戳

1.获取Token

1.通过SDK获取

AccessToken token = new AccessToken("your akID", "your akSecret");
token.apply();
String accessToken = token.getToken();
long expireTime = token.getExpireTime();

2.通过CommonRequest获取

使用阿里云公共SDK获取Access token,建议采用RPC风格的API调用.发起一次RPC风格的CommonAPI请求,您需要提供以下几个参数:

参数名参数值说明
domainnls-meta.cn-shanghai.aliyuncs.com即该产品的通用访问域名,固定值
region_idcn-shanghai服务的地域ID,固定值
actionCreateToken该API的名称,固定值
version2019-02-28该API的版本号,固定值
//1.添加Java依赖
<dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId><version>3.7.1</version>
</dependency>
<!-- http://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.49</version>
</dependency>
//2.调用服务
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.http.ProtocolType;
import com.aliyuncs.profile.DefaultProfile;
import java.text.SimpleDateFormat;
import java.util.Date;
public class CreateTokenDemo {// 您的地域IDprivate static final String REGIONID = "cn-shanghai";// 获取Token服务域名private static final String DOMAIN = "nls-meta.cn-shanghai.aliyuncs.com";// API 版本private static final String API_VERSION = "2019-02-28";// API名称private static final String REQUEST_ACTION = "CreateToken";// 响应参数private static final String KEY_TOKEN = "Token";private static final String KEY_ID = "Id";private static final String KEY_EXPIRETIME = "ExpireTime";public static void main(String args[]) throws ClientException {if (args.length < 2) {System.err.println("CreateTokenDemo need params: <AccessKey Id> <AccessKey Secret>");System.exit(-1);}String accessKeyId = args[0];String accessKeySecret = args[1];// 创建DefaultAcsClient实例并初始化DefaultProfile profile = DefaultProfile.getProfile(REGIONID,accessKeyId,accessKeySecret);IAcsClient client = new DefaultAcsClient(profile);CommonRequest request = new CommonRequest();request.setDomain(DOMAIN);request.setVersion(API_VERSION);request.setAction(REQUEST_ACTION);request.setMethod(MethodType.POST);request.setProtocol(ProtocolType.HTTPS);CommonResponse response = client.getCommonResponse(request);System.out.println(response.getData());if (response.getHttpStatus() == 200) {JSONObject result = JSON.parseObject(response.getData());String token = result.getJSONObject(KEY_TOKEN).getString(KEY_ID);long expireTime = result.getJSONObject(KEY_TOKEN).getLongValue(KEY_EXPIRETIME);System.out.println("获取到的Token: " + token + ",有效期时间戳(单位:秒): " + expireTime);// 将10位数的时间戳转换为北京时间String expireDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(expireTime * 1000));System.out.println("Token有效期的北京时间:" + expireDate);}else {System.out.println("获取Token失败!");}}
}

2.Token协议说明

如何实现获取Access Token的客户端程序?

客户端向服务端发送获取Token的请求,服务端返回创建Token结果的响应.客户端发送的请求支持使用HTTP或者HTTPS协议,请求方法支持GET或者POST方法.服务端提供了基于阿里云POP协议的接口,因此客户端需要实现阿里云POP的签名机制

由于HTTPS协议的请求参数设置与HTTP协议相同,下面将以HTTP协议请求为例,介绍如何发送获取Token请求

1.URL

协议URL方法
HTTP/1.1http://nls-meta.cn-shanghai.aliyuncs.com/GET 或 POST

2.请求参数

  • 使用GET方法,需要将请求参数设置到请求行中:/?请求参数字符串

  • 使用POST方法,需要将请求参数设置到请求的Body中

名称类型是否必需说明
AccessKeyIdString阿里云颁发给您的访问服务所用的密钥ID,请填入您的阿里云账号的AccessKey ID
ActionStringPOP API名称:CreateToken
VersionStringPOP API版本:2019-02-28
FormatString响应返回的类型:JSON
RegionIdString服务所在的地域ID:cn-shanghai
TimestampString请求的时间戳.日期格式按照ISO 8601标准表示,并需要使用UTC时间,时区为:+0(请勿使用本地时间,如北京时间).格式为YYYY-MM-DDThh:mm:ssZ.例如2019-04-03T06:15:03Z 为UTC时间2019年4月3日6点15分03秒.
SignatureMethodString签名算法:HMAC-SHA1
SignatureVersionString签名算法版本:1.0
SignatureNonceString唯一随机数uuid,用于请求的防重放攻击,每次请求唯一,不能重复使用.格式为xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx(8-4-4-4-12),例如8d1e6a7a-f44e-40d5-aedb-fe4a1c80f434
SignatureString由所有请求参数计算出的签名结果,生成方法请参考下文签名机制.

3.HTTP请求头部

HTTP 请求头部由“关键字/值”对组成,每行一对,关键字和值用英文冒号“:”分隔,设置内容为如下表格:

名称类型是否必需描述
HostStringHTTP请求的服务器域名:nls-meta.cn-shanghai.aliyuncs.com,一般会根据请求链接自动解析
AcceptString指定客户端能够接收的内容类型:application/json,不设置默认为 /
Content-typeStringPOST方法必须设置指定POST方法请求的Body数据格式:application/x-www-form-urlencoded

报文示例

1.HTTP GET请求报文

GET /?Signature=O0s6pfeOxtFM6YKSZKQdSyPR9Vs%3D&AccessKeyId=LTA******F3s&Action=CreateToken&Format=JSON&RegionId=cn-shanghai&SignatureMethod=HMAC-SHA1&SignatureNonce=a1f01895-6ff1-43c1-ba15-6c109fa00106&SignatureVersion=1.0&Timestamp=2019-03-27T09%3A51%3A25Z&Version=2019-02-28 HTTP/1.1
Host: nls-meta.cn-shanghai.aliyuncs.com
User-Agent: curl/7.49.1
Accept: */*

2.HTTP POST请求报文

POST / HTTP/1.1
Host: nls-meta.cn-shanghai.aliyuncs.com
User-Agent: curl/7.49.1
Accept: */*
Content-type: application/x-www-form-urlencoded
Content-Length: 276
SignatureVersion=1.0&Action=CreateToken&Format=JSON&SignatureNonce=8d1e6a7a-f44e-40d5-aedb-fe4a1c80f434&Version=2019-02-28&AccessKeyId=LTA******F3s&Signature=oT8A8RgvFE1tMD%2B3hDbGuoMQSi8%3D&SignatureMethod=HMAC-SHA1&RegionId=cn-shanghai&Timestamp=2019-03-25T09%3A07%3A52Z

4.响应结果

发送获取Token的HTTP请求之后,会收到服务端的响应,结果以JSON字符串的形式保存在该响应中.GET方法和POST方法的响应结果相同

  • 成功响应HTTP状态码为200,响应字段说明:

参数类型说明
Tokentoken对象包含了具体的token值和有效期时间戳
IdString本次请求分配的token值
ExpireTimeLongtoken的有效期时间戳(单位:秒,例如1553825814换算为北京时间为:2019/3/29 10:16:54,即token在该时间之前有效.)
HTTP/1.1 200 OK
Date: Mon, 25 Mar 2019 09:29:24 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 216
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-Requested-With, X-Sequence, _aop_secret, _aop_signature
Access-Control-Max-Age: 172800
Server: Jetty(7.2.2.v20101205)
{"NlsRequestId":"dd05a301b40441c99a2671905325fb1f","RequestId":"E11F2DC2-0163-4D97-A704-0BD28045608A","ErrMsg":"","Token":{"ExpireTime":1553592564,"Id":"889******166","UserId":"150**********151"}}
  • 失败响应HTTP状态码为非200,响应字段说明:

参数类型说明
RequestIdString请求ID
MessageString失败响应的错误信息
CodeString失败响应的错误码

说明: 请根据错误码和错误信息提示检查请求参数是否设置正确,如不能排查,请将响应信息提交到工单

以重传入阿里云账号的AccessKey Id错误为例:

HTTP/1.1 404 Not Found
Date: Thu, 28 Mar 2019 07:23:01 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 290
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-Requested-With, X-Sequence, _aop_secret, _aop_signature
Access-Control-Max-Age: 172800
Server: Jetty(7.2.2.v20101205)
{"Recommend":"https://error-center.aliyun.com/status/search?Keyword=InvalidAccessKeyId.NotFound&source=PopGw","Message":"Specified access key is not found.","RequestId":"A51587CB-5193-4DB8-9AED-CD4365C2D1E1","HostId":"nls-meta.cn-shanghai.aliyuncs.com","Code":"InvalidAccessKeyId.NotFound"}

3.签名机制

服务端POP API对每个接口访问请求的发送者都要进行身份验证,所以无论使用HTTP协议还是HTTPS协议提交的请求,都需要在请求中包含签名信息.通过签名机制,服务端可以确认哪位客户在做API请求,并能确认客户请求在网络传输过程中有无被篡改

1.安全验证流程

计算签名时,需要您的阿里云账号的AccessKeyId 和 AccessKeySecret,使用HMAC-SHA1算法进行对称加密.其工作流程如下:

  1. 请求端根据API请求内容(包括HTTP 请求参数和Body)生成签名字符串

  2. 请求端使用阿里云账号的AccessKeyId 和 AccessKeySecret对第一步生成的签名字符串进行签名,获得该API请求的数字签名

  3. 请求端把API请求内容和数字签名一同发送给服务端

  4. 服务端在接收到请求后会重复如上的第1、2步工作(服务端会在后台获取该请求使用的用户秘钥)并在服务端计算出该请求期望的数字签名

  5. 服务端用期望的数字签名和请求端发送过来的数字签名做对比,如果完全一致则认为该请求通过验证.否则直接拒绝该请求

2.生成请求的签名字符串

1. 构造规范化的请求字符串

将HTTP的请求参数(不包括Signature)构造成规范化的请求字符串.规范化步骤:

  1. 参数排序.按参数名的字典顺序,对请求参数进行排序,严格按照大小写敏感排序

  2. 编码参数,对排序后的请求参数进行规范化设置. 请求参数的名称和值都要使用UTF-8字符集进行URL编码,URL编码规则如下:

    • 对于字符 A-Z、a-z、0-9以及字符-_.~不编码

    • 对于其他字符编码成“%XY”的格式,其中XY是字符对应ASCII码的16进制表示.比如英文的双引号(”)对应的编码就是%22

    • 对于扩展的UTF-8字符,编码成“%XY%ZA…”的格式

    • 需要说明的是英文空格要被编码是%20,而不是加号+;注:一般支持URL编码的库(比如Java中的java.net.URLEncoder)都是按照“application/x-www-form-urlencoded”的MIME类型的规则进行编码的.实现时可以直接使用此类方式进行编码,然后把编码后的字符串中:加号+替换为%20,星号*替换为%2A,%7E替换为波浪号~,即可得到上述规则描述的编码字符串

  3. 使用等号=连接URL编码后的参数名和参数值:percentEncode(参数Key) + “=” + percentEncode(参数值)

  4. 使用与号&连接第4步URL编码后的请求参数对,例如Action=CreateToken&Format=JSON

  5. 返回规范化的请求字符串(注意:字符串中第一个参数名前面不需要 & 符号)

构造规范化的请求字符串代码示例:

String percentEncode(String value) throws UnsupportedEncodingException {return value != null ? URLEncoder.encode(value, URL_ENCODING).replace("+", "%20").replace("*", "%2A").replace("%7E", "~") : null;
}
// 对参数Key排序
String[] sortedKeys = queryParamsMap.keySet().toArray(new String[] {});
Arrays.sort(sortedKeys);
// 对排序的参数进行编码、拼接
for (String key : sortedKeys) {canonicalizedQueryString.append("&").append(percentEncode(key)).append("=").append(percentEncode(queryParamsMap.get(key)));
}
queryString = canonicalizedQueryString.toString().substring(1);

构造规范化的请求字符串:

AccessKeyId=LTA******3s2&Action=CreateToken&Format=JSON&RegionId=cn-shanghai&SignatureMethod=HMAC-SHA1&SignatureNonce=f20b1beb-e5dc-4245-9e96-aa582e905c1a&SignatureVersion=1.0&Timestamp=2019-04-03T03%3A40%3A13Z&Version=2019-02-28

2. 构造待签名字符串

将HTTP请求的方法(GET)、URL编码的URL路径(/)、URL编码的第1步获取的规范化的请求字符串使用与符号&连接成待签名字符串:

HTTPMethod + "&" + percentEncode("/") + "&" + percentEncode(queryString)

构造签名字符串代码示例:

StringBuilder strBuilderSign = new StringBuilder();
strBuilderSign.append(HTTPMethod);
strBuilderSign.append("&");
strBuilderSign.append(percentEncode(urlPath));
strBuilderSign.append("&");
strBuilderSign.append(percentEncode(queryString));
stringToSign = strBuilderSign.toString();

构造的签名字符串:strToSign

3. 计算签名

  • 签名采用HMAC-SHA1算法 + Base64,编码采用UTF-8

  • 根据您的AccessKeySecret,将第2步构造的待签名字符串使用HMAC-SHA1算法计算出对应的数字签名.其中,计算签名时使用的AccessKeySecret必须在其后面增加一个与字符&

  • 签名也要做URL编码

计算签名的代码示例:

signature = Base64( HMAC-SHA1(stringToSign, accessKeySecret + "&") );// 进行URL编码signature = percentEncode(signature)

计算得到的签名:

# 签名串AKIktdPUMCV12fTh667BLXeuCtg=# URL编码后的结果AKIktdPUMCV12fTh667BLXeuCtg%3D

计算签名后,将签名的键值对用符号=连接,并使用符号&添加到第1步获取的请求字符串中,作为HTTP GET请求参数,发送到服务端,获取token

String queryStringWithSign = "Signature=" + signature + "&" + queryString;

3.Java Demo

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.SimpleTimeZone;
import java.util.UUID;
public class CreateToken {private final static String TIME_ZONE = "GMT";private final static String FORMAT_ISO8601 = "yyyy-MM-dd'T'HH:mm:ss'Z'";private final static String URL_ENCODING = "UTF-8";private static final String ALGORITHM_NAME = "HmacSHA1";private static final String ENCODING = "UTF-8";private static String token = null;private static long expireTime = 0;/*** 获取时间戳* 必须符合ISO8601规范,并需要使用UTC时间,时区为+0*/public static String getISO8601Time(Date date) {Date nowDate = date;if (null == date) {nowDate = new Date();}SimpleDateFormat df = new SimpleDateFormat(FORMAT_ISO8601);df.setTimeZone(new SimpleTimeZone(0, TIME_ZONE));return df.format(nowDate);}/*** 获取UUID*/public static String getUniqueNonce() {UUID uuid = UUID.randomUUID();return uuid.toString();}/*** URL编码* 使用UTF-8字符集按照 RFC3986 规则编码请求参数和参数取值*/public static String percentEncode(String value) throws UnsupportedEncodingException {return value != null ? URLEncoder.encode(value, URL_ENCODING).replace("+", "%20").replace("*", "%2A").replace("%7E", "~") : null;}/**** 将参数排序后,进行规范化设置,组合成请求字符串* @param queryParamsMap   所有请求参数* @return 规范化的请求字符串*/public static String canonicalizedQuery( Map<String, String> queryParamsMap) {String[] sortedKeys = queryParamsMap.keySet().toArray(new String[] {});Arrays.sort(sortedKeys);String queryString = null;try {StringBuilder canonicalizedQueryString = new StringBuilder();for (String key : sortedKeys) {canonicalizedQueryString.append("&").append(percentEncode(key)).append("=").append(percentEncode(queryParamsMap.get(key)));}queryString = canonicalizedQueryString.toString().substring(1);System.out.println("规范化后的请求参数串:" + queryString);} catch (UnsupportedEncodingException e) {System.out.println("UTF-8 encoding is not supported.");e.printStackTrace();}return queryString;}/**** 构造签名字符串* @param method       HTTP请求的方法* @param urlPath      HTTP请求的资源路径* @param queryString  规范化的请求字符串* @return 签名字符串*/public static String createStringToSign(String method, String urlPath, String queryString) {String stringToSign = null;try {StringBuilder strBuilderSign = new StringBuilder();strBuilderSign.append(method);strBuilderSign.append("&");strBuilderSign.append(percentEncode(urlPath));strBuilderSign.append("&");strBuilderSign.append(percentEncode(queryString));stringToSign = strBuilderSign.toString();System.out.println("构造的签名字符串:" + stringToSign);} catch (UnsupportedEncodingException e) {System.out.println("UTF-8 encoding is not supported.");e.printStackTrace();}return stringToSign;}/**** 计算签名* @param stringToSign      签名字符串* @param accessKeySecret   阿里云AccessKey Secret加上与号&* @return 计算得到的签名*/public static String sign(String stringToSign, String accessKeySecret) {try {Mac mac = Mac.getInstance(ALGORITHM_NAME);mac.init(new SecretKeySpec(accessKeySecret.getBytes(ENCODING),ALGORITHM_NAME));byte[] signData = mac.doFinal(stringToSign.getBytes(ENCODING));String signBase64 = DatatypeConverter.printBase64Binary(signData);System.out.println("计算的得到的签名:" + signBase64);String signUrlEncode = percentEncode(signBase64);System.out.println("UrlEncode编码后的签名:" + signUrlEncode);return signUrlEncode;} catch (NoSuchAlgorithmException e) {throw new IllegalArgumentException(e.toString());} catch (UnsupportedEncodingException e) {throw new IllegalArgumentException(e.toString());} catch (InvalidKeyException e) {throw new IllegalArgumentException(e.toString());}}/**** 发送HTTP GET请求,获取token和有效期时间戳* @param queryString 请求参数*/public static void processGETRequest(String queryString) {/*** 设置HTTP GET请求* 1. 使用HTTP协议* 2. Token服务域名:nls-meta.cn-shanghai.aliyuncs.com* 3. 请求路径:/* 4. 设置请求参数*/String url = "http://nls-meta.cn-shanghai.aliyuncs.com";url = url + "/";url = url + "?" + queryString;System.out.println("HTTP请求链接:" + url);Request request = new Request.Builder().url(url).header("Accept", "application/json").get().build();try {OkHttpClient client = new OkHttpClient();Response response = client.newCall(request).execute();String result = response.body().string();if (response.isSuccessful()) {JSONObject rootObj = JSON.parseObject(result);JSONObject tokenObj = rootObj.getJSONObject("Token");if (tokenObj != null) {token = tokenObj.getString("Id");expireTime = tokenObj.getLongValue("ExpireTime");}else{System.err.println("提交获取Token请求失败: " + result);}}else {System.err.println("提交获取Token请求失败: " + result);}response.close();} catch (Exception e) {e.printStackTrace();}}public static void main(String args[]) {if (args.length < 2) {System.err.println("CreateTokenDemo need params: <AccessKey Id> <AccessKey Secret>");System.exit(-1);}String accessKeyId = args[0];String accessKeySecret = args[1];System.out.println(getISO8601Time(null));// 所有请求参数Map<String, String> queryParamsMap = new HashMap<String, String>();queryParamsMap.put("AccessKeyId", accessKeyId);queryParamsMap.put("Action", "CreateToken");queryParamsMap.put("Version", "2019-02-28");queryParamsMap.put("Timestamp", getISO8601Time(null));queryParamsMap.put("Format", "JSON");queryParamsMap.put("RegionId", "cn-shanghai");queryParamsMap.put("SignatureMethod", "HMAC-SHA1");queryParamsMap.put("SignatureVersion", "1.0");queryParamsMap.put("SignatureNonce", getUniqueNonce());/*** 1.构造规范化的请求字符串*/String queryString = canonicalizedQuery(queryParamsMap);if (null == queryString) {System.out.println("构造规范化的请求字符串失败!");return;}/*** 2.构造签名字符串*/String method = "GET";  // 发送请求的 HTTP 方法,GETString urlPath = "/";   // 请求路径String stringToSign = createStringToSign(method, urlPath, queryString);if (null == stringToSign) {System.out.println("构造签名字符串失败");return;}/*** 3.计算签名*/String signature = sign(stringToSign, accessKeySecret + "&");if (null == signature) {System.out.println("计算签名失败!");return;}/*** 4.将签名加入到第1步获取的请求字符串*/String queryStringWithSign = "Signature=" + signature + "&" + queryString;System.out.println("带有签名的请求字符串:" + queryStringWithSign);/*** 5.发送HTTP GET请求,获取token*/processGETRequest(queryStringWithSign);if (token != null) {System.out.println("获取的Token:" + token + ", 有效期时间戳(秒):" + expireTime);// 将10位数的时间戳转换为北京时间String expireDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(expireTime * 1000));System.out.println("Token有效期的北京时间:" + expireDate);}}
}

三.服务技术

服务时效性功能适用场景
一句话识别实时识别识别一分钟内的短语音APP语音搜索、语音电话客服、对话聊天、控制口令等场景
实时语音识别实时识别识别长时间的语音数据流会议演讲、视频直播等长时间不间断的场景
语音合成实时合成合成长度不超过300个字符(UTF-8编码)的文本内容需要人工合成音的场景
录音文件识别24小时内完成识别,非实时识别识别文件大小不超过512MB非实时识别场景

说明:

  • 除录音文件识别以外的其他识别服务只支持单声道(mono)语音数据

  • 识别服务只支持8000Hz/16000Hz的采样率,16bit的采样位数的音频

1.一句话识别

1.Java SDK 2.0

可从maven 服务器下载最新版本SDK:

<dependency><groupId>com.alibaba.nls</groupId><artifactId>nls-sdk-recognizer</artifactId><version>2.1.1</version>
</dependency>

1.服务验证

java -cp nls-example-recognizer-2.0.0-jar-with-dependencies.jar com.alibaba.nls.client.SpeechRecognizerDemo

2.服务压测

java -jar nls-example-recognizer-2.0.0-jar-with-dependencies.jar

3.关键接口

  • NlsClient:语音处理client,相当于所有语音相关处理类的factory,全局创建一个实例即可.线程安全.

  • SpeechRecognizer:一句话识别处理类,设置请求参数,发送请求及声音数据.非线程安全.

  • SpeechRecognizerListener:识别结果监听类,监听识别结果.非线程安全.

4.SDK 调用注意事项

  1. NlsClient对象创建一次可以重复使用,每次创建消耗性能.NlsClient使用了netty的框架,创建时比较消耗时间和资源,但创建之后可以重复利用.建议调用程序将NlsClient的创建和关闭与程序本身的生命周期结合

  2. SpeechRecognizer对象不能重复使用,一个识别任务对应一个SpeechRecognizer对象.例如有N个音频文件,则要进行N次识别任务,创建N个SpeechRecognizer对象.

  3. 实现的SpeechRecognizerListener对象和SpeechRecognizer对象是一一对应的,不能将一个SpeechRecognizerListener对象设置到多个SpeechRecognizer对象中,否则不能区分是哪个识别任务

  4. Java SDK依赖了Netty网络库,版本需设置为4.1.17.Final及以上.如果您的应用中依赖了Netty,请确保版本符合要求

5.Java Demo

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import com.alibaba.nls.client.protocol.InputFormatEnum;
import com.alibaba.nls.client.protocol.NlsClient;
import com.alibaba.nls.client.protocol.SampleRateEnum;
import com.alibaba.nls.client.protocol.asr.SpeechRecognizer;
import com.alibaba.nls.client.protocol.asr.SpeechRecognizerListener;
import com.alibaba.nls.client.protocol.asr.SpeechRecognizerResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*** 此示例演示了*      ASR一句话识别API调用*      动态获取token*      通过本地文件模拟实时流发送*      识别耗时计算* (仅作演示,需用户根据实际情况实现)*/
public class SpeechRecognizerDemo {private static final Logger logger = LoggerFactory.getLogger(SpeechRecognizerDemo.class);private String appKey;NlsClient client;public SpeechRecognizerDemo(String appKey, String id, String secret, String url) {this.appKey = appKey;//TODO 重要提示 创建NlsClient实例,应用全局创建一个即可,生命周期可和整个应用保持一致,默认服务地址为阿里云线上服务地址//TODO 这里简单演示了获取token 的代码,该token会过期,实际使用时注意在accessToken.getExpireTime()过期前再次获取tokenAccessToken accessToken = new AccessToken(id, secret);try {accessToken.apply();System.out.println("get token: " + accessToken.getToken() + ", expire time: " + accessToken.getExpireTime());// TODO 创建NlsClient实例,应用全局创建一个即可if(url.isEmpty()) {client = new NlsClient(accessToken.getToken());}else {client = new NlsClient(url, accessToken.getToken());}} catch (IOException e) {e.printStackTrace();}}private static SpeechRecognizerListener getRecognizerListener(int myOrder, String userParam) {SpeechRecognizerListener listener = new SpeechRecognizerListener() {//识别出中间结果.服务端识别出一个字或词时会返回此消息.仅当setEnableIntermediateResult(true)时,才会有此类消息返回@Overridepublic void onRecognitionResultChanged(SpeechRecognizerResponse response) {//事件名称 RecognitionResultChanged、 状态码(20000000 表示识别成功)、语音识别文本System.out.println("name: " + response.getName() + ", status: " + response.getStatus() + ", result: " + response.getRecognizedText());}//识别完毕@Overridepublic void onRecognitionCompleted(SpeechRecognizerResponse response) {//事件名称 RecognitionCompleted, 状态码 20000000 表示识别成功, getRecognizedText是识别结果文本System.out.println("name: " + response.getName() + ", status: " + response.getStatus() + ", result: " + response.getRecognizedText());}@Overridepublic void onStarted(SpeechRecognizerResponse response) {System.out.println("myOrder: " + myOrder + "; myParam: " + userParam + "; task_id: " + response.getTaskId());}@Overridepublic void onFail(SpeechRecognizerResponse response) {// TODO 重要提示: task_id很重要,是调用方和服务端通信的唯一ID标识,当遇到问题时,需要提供此task_id以便排查System.out.println("task_id: " + response.getTaskId() + ", status: " + response.getStatus() + ", status_text: " + response.getStatusText());}};return listener;}/// 根据二进制数据大小计算对应的同等语音长度/// sampleRate 仅支持8000或16000public static int getSleepDelta(int dataSize, int sampleRate) {// 仅支持16位采样int sampleBytes = 16;// 仅支持单通道int soundChannel = 1;return (dataSize * 10 * 8000) / (160 * sampleRate);}public void process(String filepath, int sampleRate) {SpeechRecognizer recognizer = null;try {// 传递用户自定义参数String myParam = "user-param";int myOrder = 1234;SpeechRecognizerListener listener = getRecognizerListener(myOrder, myParam);recognizer = new SpeechRecognizer(client, listener);recognizer.setAppKey(appKey);//设置音频编码格式 TODO 如果是opus文件,请设置为 InputFormatEnum.OPUSrecognizer.setFormat(InputFormatEnum.PCM);//设置音频采样率if(sampleRate == 16000) {recognizer.setSampleRate(SampleRateEnum.SAMPLE_RATE_16K);} else if(sampleRate == 8000) {recognizer.setSampleRate(SampleRateEnum.SAMPLE_RATE_8K);}//设置是否返回中间识别结果recognizer.setEnableIntermediateResult(true);//此方法将以上参数设置序列化为json发送给服务端,并等待服务端确认long now = System.currentTimeMillis();recognizer.start();logger.info("ASR start latency : " + (System.currentTimeMillis() - now) + " ms");File file = new File(filepath);FileInputStream fis = new FileInputStream(file);byte[] b = new byte[3200];int len;while ((len = fis.read(b)) > 0) {logger.info("send data pack length: " + len);recognizer.send(b);// TODO  重要提示:这里是用读取本地文件的形式模拟实时获取语音流并发送的,因为read很快,所以这里需要sleep// TODO  如果是真正的实时获取语音,则无需sleep, 如果是8k采样率语音,第二个参数改为8000int deltaSleep = getSleepDelta(len, sampleRate);Thread.sleep(deltaSleep);}//通知服务端语音数据发送完毕,等待服务端处理完成now = System.currentTimeMillis();// TODO 计算实际延迟: stop返回之后一般即是识别结果返回时间logger.info("ASR wait for complete");recognizer.stop();logger.info("ASR stop latency : " + (System.currentTimeMillis() - now) + " ms");fis.close();} catch (Exception e) {System.err.println(e.getMessage());} finally {//关闭连接if (null != recognizer) {recognizer.close();}}}public void shutdown() {client.shutdown();}public static void main(String[] args) throws Exception {String appKey = null; // "填写你的appkey";String id = null; // "填写你在阿里云网站上的AccessKeyId";String secret = null; // "填写你在阿里云网站上的AccessKeySecret";String url = ""; // 默认即可,默认值:wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1if (args.length == 3) {appKey   = args[0];id       = args[1];secret   = args[2];} else if (args.length == 4) {appKey   = args[0];id       = args[1];secret   = args[2];url      = args[3];} else {System.err.println("run error, need params(url is optional): " + "<app-key> <AccessKeyId> <AccessKeySecret> [url]");System.exit(-1);}SpeechRecognizerDemo demo = new SpeechRecognizerDemo(appKey, id, secret, url);// TODO 重要提示: 这里用一个本地文件来模拟发送实时流数据,实际使用时,用户可以从某处实时采集或接收语音流并发送到ASR服务端demo.process("./nls-sample-16k.wav", 16000);//demo.process("./nls-sample.opus", 16000);demo.shutdown();}
}

2.RESTful API 2.0

1.功能介绍

一句话识别RESTful API支持以POST方式整段上传不超过一分钟的语音文件.识别结果将以JSON格式在请求响应中一次性返回,开发者需要保证在识别结果返回之前连接不被中断

  • 支持音频编码格式:pcm(无压缩的pcm文件或wav文件)、opus,16bit采样位数的单声道(mono)

  • 支持音频采样率:8000Hz、16000Hz

  • 支持对返回结果进行设置:是否在后处理中添加标点,是否将中文数字转为阿拉伯数字输出

  • 支持控制台配置项目热词和自学习模型训练

  • 支持多种语言的识别,可在控制台编辑项目进行模型配置

说明:所有服务端的响应都会在返回信息中包含task_id参数,用于表示本次识别任务的ID,请记录下这个值,如果发生错误,请将task_id和错误信息提交到工单

2.服务地址

访问类型说明URLHost
外网访问所有服务器均可使用外网访问URLhttp://nls-gateway.cn-shanghai.aliyuncs.com/stream/v1/asrnls-gateway.cn-shanghai.aliyuncs.com
阿里云上海ECS内网访问您使用阿里云上海ECS(即ECS地域为华东2(上海)),可使用内网访问URL 说明:使用内网访问方式,将不产生ECS实例的公网流量费用. ECS的经典网络不能访问AnyTunnel,即不能在内网访问语音服务;如果希望使用AnyTunnel,需要创建专有网络后在其内部访问.http://nls-gateway.cn-shanghai-internal.aliyuncs.com/stream/v1/asrnls-gateway.cn-shanghai-internal.aliyuncs.com

3.上传音频文件

一句话识别请求HTTP报文实例:

POST /stream/v1/asr?appkey=23****f5&format=pcm&sample_rate=16000&enable_punctuation_prediction=true&enable_inverse_text_normalization=true HTTP/1.1X-NLS-Token: 450372e4279******bcc2b3c793Content-type: application/octet-streamContent-Length: 94616Host: nls-gateway.cn-shanghai.aliyuncs.com[audio data]

一个完整的一句话识别RESTful API请求需包含以下要素:

1.HTTP 请求行

  • URL

协议URL方法
HTTP/1.1http://nls-gateway.cn-shanghai.aliyuncs.com/stream/v1/asrPOST
  • 请求参数

ParameterTypeDescription
appkeyString应用appkey,必填
formatString音频编码格式,可选,支持的格式:pcm、opus,默认是pcm
sample_rateInteger音频采样率,可选,16000Hz或者8000Hz,默认是16000Hz
vocabulary_idString指定添加热词表ID,可选,默认不添加
customization_idString指定添加自学习模型ID,可选,默认不添加
enable_punctuation_predictionBoolean是否在后处理中添加标点,可选,true或者false,默认false不开启
enable_inverse_text_normalizationBoolean是否在后处理中执行ITN,可选,true或者false,默认false不开启
enable_voice_detectionBoolean是否启动语音检测,可选,true或者false,默认false不开启.说明:如果开启语音检测,服务端会对上传的音频进行静音检测,切除静音部分和之后的语音内容,不再对其进行识别;不同的模型表现结果不同.

示例:

http://nls-gateway.cn-shanghai.aliyuncs.com/stream/v1/asr?appkey=Yu1******uncS&format=pcm&sample_rate=16000&vocabulary_id=a17******d6b&customization_id=abd******ed8&enable_punctuation_prediction=true&enable_inverse_text_normalization=true&enable_voice_detection=true

2.HTTP 请求头部

HTTP 请求头部由“关键字/值”对组成,每行一对,关键字和值用英文冒号“:”分隔,设置内容为如下表格:

名称类型需求描述
X-NLS-TokenString必填服务鉴权Token,获取方法请阅读获取 Token一节
Content-typeString必填必须为“application/octet-stream”,表明HTTP body的数据为二进制流
Content-Lengthlong必填HTTP body中请求数据的长度,即音频文件的长度
HostString必填HTTP请求的服务器域名,必须为“nls-gateway.cn-shanghai.aliyuncs.com”

3.HTTP 请求体

HTTP请求体传入的是二进制音频数据,因此在HTTP请求头部中的Content-Type必须设置为application/octet-stream

4.响应结果

发送上传音频的HTTP请求之后,会收到服务端的响应,识别的结果以JSON字符串的形式保存在该响应中

1.成功响应

{    "task_id": "cf7b0c5339244ee29cd4e43fb97fd52e",    "result": "北京的天气.",    "status":20000000,    "message":"SUCCESS"
}

2.失败响应

以鉴权token错误为例:

{    "task_id": "8bae3613dfc54ebfa811a17d8a7a9ae7",    "result": "",    "status": 40000001,    "message": "Gateway:ACCESS_DENIED:The token 'c0c1e860f3*******de8091c68a' is invalid!"
}

响应字段说明

ParameterTypeDescription
task_idString32位任务ID,请记录该值,以便于排查错误
resultString语音识别结果
statusInteger服务状态码
messageString服务状态描述

服务状态码说明

20000000表示成功,4开头的状态码表示客户端的错误,5开头的错误码表示服务端的错误

服务状态码服务状态描述解决方案
20000000请求成功 
40000000默认的客户端错误码查看错误消息或提交工单
40000001身份认证失败检查使用的令牌是否正确,是否过期
40000002无效的消息检查发送的消息是否符合要求
40000003无效的参数检查参数值设置是否合理
40000004空闲超时确认是否长时间没有发送数据掉服务端
40000005请求数量过多检查是否超过了并发连接数或者每秒钟请求数
   
50000000默认的服务端错误如果偶现可以忽略,重复出现请提交工单
50000001内部GRPC调用错误如果偶现可以忽略,重复出现请提交工单

5.Java Demo

import com.alibaba.fastjson.JSONPath;
import com.alibaba.nls.client.example.utils.HttpUtil;
import java.util.HashMap;
public class SpeechRecognizerRESTfulDemo {private String accessToken;private String appkey;public SpeechRecognizerRESTfulDemo(String appkey, String token) {this.appkey = appkey;this.accessToken = token;}public void process(String fileName, String format, int sampleRate,boolean enablePunctuationPrediction,boolean enableInverseTextNormalization,boolean enableVoiceDetection) {/*** 设置HTTP REST POST请求* 1.使用http协议* 2.语音识别服务域名:nls-gateway.cn-shanghai.aliyuncs.com* 3.语音识别接口请求路径:/stream/v1/asr* 4.设置必须请求参数:appkey、format、sample_rate,* 5.设置可选请求参数:enable_punctuation_prediction、enable_inverse_text_normalization、enable_voice_detection*/String url = "http://nls-gateway.cn-shanghai.aliyuncs.com/stream/v1/asr";String request = url;request = request + "?appkey=" + appkey;request = request + "&format=" + format;request = request + "&sample_rate=" + sampleRate;if (enablePunctuationPrediction) {request = request + "&enable_punctuation_prediction=" + true;}if (enableInverseTextNormalization) {request = request + "&enable_inverse_text_normalization=" + true;}if (enableVoiceDetection) {request = request + "&enable_voice_detection=" + true;}System.out.println("Request: " + request);/*** 设置HTTP 头部字段* 1.鉴权参数* 2.Content-Type:application/octet-stream*/HashMap<String, String> headers = new HashMap<String, String>();headers.put("X-NLS-Token", this.accessToken);headers.put("Content-Type", "application/octet-stream");/*** 发送HTTP POST请求,返回服务端的响应*/String response = HttpUtil.sendPostFile(request, headers, fileName);if (response != null) {System.out.println("Response: " + response);String result = JSONPath.read(response, "result").toString();System.out.println("识别结果:" + result);}else {System.err.println("识别失败!");}}public static void main(String[] args) {if (args.length < 2) {System.err.println("SpeechRecognizerRESTfulDemo need params: <token> <app-key>");System.exit(-1);}String token = args[0];String appkey = args[1];SpeechRecognizerRESTfulDemo demo = new SpeechRecognizerRESTfulDemo(appkey, token);String fileName = SpeechRecognizerRESTfulDemo.class.getClassLoader().getResource("./nls-sample-16k.wav").getPath();String format = "pcm";int sampleRate = 16000;boolean enablePunctuationPrediction = true;boolean enableInverseTextNormalization = true;boolean enableVoiceDetection = false;demo.process(fileName, format, sampleRate, enablePunctuationPrediction, enableInverseTextNormalization, enableVoiceDetection);}
}

2.实时语音识别

1.Java SDK2.0

可从maven 服务器下载最新版本SDK:

<dependency>    <groupId>com.alibaba.nls</groupId>    <artifactId>nls-sdk-transcriber</artifactId>    <version>2.1.1</version>
</dependency>

1.服务验证

java -cp nls-example-transcriber-2.0.0-jar-with-dependencies.jar com.alibaba.nls.client.SpeechTranscriberDem

2.服务压测

java -jar nls-example-transcriber-2.0.0-jar-with-dependencies.jar

3.关键接口

  • NlsClient:语音处理client,相当于所有语音相关处理类的factory,全局创建一个实例即可.线程安全

  • SpeechTranscriber:实时语音识别类,设置请求参数,发送请求及声音数据.非线程安全

  • SpeechTranscriberListener:实时语音识别结果监听类,监听识别结果.非线程安全

4.SDK 调用注意事项

  1. NlsClient对象创建一次可以重复使用,每次创建消耗性能.NlsClient使用了netty的框架,创建时比较消耗时间和资源,但创建之后可以重复利用.建议调用程序将NlsClient的创建和关闭与程序本身的生命周期结合

  2. SpeechTranscriber对象不能重复使用,一个识别任务对应一个SpeechTranscriber对象.例如有N个音频文件,则要进行N次识别任务,创建N个SpeechTranscriber对象

  3. 实现的SpeechTranscriberListener对象和SpeechTranscriber对象是一一对应的,不能将一个SpeechTranscriberListener对象设置到多个SpeechTranscriber对象中,否则不能区分是哪个识别任务

  4. Java SDK依赖了Netty网络库,版本需设置为4.1.17.Final及以上.如果您的应用中依赖了Netty,请确保版本符合要求

5.Java Demo

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import com.alibaba.nls.client.protocol.InputFormatEnum;
import com.alibaba.nls.client.protocol.NlsClient;
import com.alibaba.nls.client.protocol.SampleRateEnum;
import com.alibaba.nls.client.protocol.asr.SpeechTranscriber;
import com.alibaba.nls.client.protocol.asr.SpeechTranscriberListener;
import com.alibaba.nls.client.protocol.asr.SpeechTranscriberResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*** 此示例演示了*      ASR实时识别API调用*      动态获取token*      通过本地模拟实时流发送*      识别耗时计算* (仅作演示,需用户根据实际情况实现)*/
public class SpeechTranscriberDemo {private String appKey;private NlsClient client;private static final Logger logger = LoggerFactory.getLogger(SpeechTranscriberDemo.class);public SpeechTranscriberDemo(String appKey, String id, String secret, String url) {this.appKey = appKey;//TODO 重要提示 创建NlsClient实例,应用全局创建一个即可,生命周期可和整个应用保持一致,默认服务地址为阿里云线上服务地址//TODO 这里简单演示了获取token 的代码,该token会过期,实际使用时注意在accessToken.getExpireTime()过期前再次获取tokenAccessToken accessToken = new AccessToken(id, secret);try {accessToken.apply();System.out.println("get token: " + ", expire time: " + accessToken.getExpireTime());// TODO 创建NlsClient实例,应用全局创建一个即可,用户指定服务地址if(url.isEmpty()) {client = new NlsClient(accessToken.getToken());}else {client = new NlsClient(url, accessToken.getToken());}} catch (IOException e) {e.printStackTrace();}}private static SpeechTranscriberListener getTranscriberListener() {SpeechTranscriberListener listener = new SpeechTranscriberListener() {//TODO 识别出中间结果.服务端识别出一个字或词时会返回此消息.仅当setEnableIntermediateResult(true)时,才会有此类消息返回@Overridepublic void onTranscriptionResultChange(SpeechTranscriberResponse response) {System.out.println("task_id: " + response.getTaskId() +", name: " + response.getName() +//状态码 20000000 表示正常识别", status: " + response.getStatus() +//句子编号,从1开始递增", index: " + response.getTransSentenceIndex() +//当前的识别结果", result: " + response.getTransSentenceText() +//当前已处理的音频时长,单位是毫秒", time: " + response.getTransSentenceTime());}@Overridepublic void onTranscriberStart(SpeechTranscriberResponse response) {// TODO 重要提示: task_id很重要,是调用方和服务端通信的唯一ID标识,当遇到问题时,需要提供此task_id以便排查System.out.println("task_id: " + response.getTaskId() + ", name: " + response.getName() + ", status: " + response.getStatus());}@Overridepublic void onSentenceBegin(SpeechTranscriberResponse response) {System.out.println("task_id: " + response.getTaskId() + ", name: " + response.getName() + ", status: " + response.getStatus());}//识别出一句话.服务端会智能断句,当识别到一句话结束时会返回此消息@Overridepublic void onSentenceEnd(SpeechTranscriberResponse response) {System.out.println("task_id: " + response.getTaskId() +", name: " + response.getName() +//状态码 20000000 表示正常识别", status: " + response.getStatus() +//句子编号,从1开始递增", index: " + response.getTransSentenceIndex() +//当前的识别结果", result: " + response.getTransSentenceText() +//置信度", confidence: " + response.getConfidence() +//开始时间", begin_time: " + response.getSentenceBeginTime() +//当前已处理的音频时长,单位是毫秒", time: " + response.getTransSentenceTime());}//识别完毕@Overridepublic void onTranscriptionComplete(SpeechTranscriberResponse response) {System.out.println("task_id: " + response.getTaskId() + ", name: " + response.getName() + ", status: " + response.getStatus());}@Overridepublic void onFail(SpeechTranscriberResponse response) {// TODO 重要提示: task_id很重要,是调用方和服务端通信的唯一ID标识,当遇到问题时,需要提供此task_id以便排查System.out.println("task_id: " + response.getTaskId() +  ", status: " + response.getStatus() + ", status_text: " + response.getStatusText());}};return listener;}/// 根据二进制数据大小计算对应的同等语音长度/// sampleRate 仅支持8000或16000public static int getSleepDelta(int dataSize, int sampleRate) {// 仅支持16位采样int sampleBytes = 16;// 仅支持单通道int soundChannel = 1;return (dataSize * 10 * 8000) / (160 * sampleRate);}public void process(String filepath) {SpeechTranscriber transcriber = null;try {//创建实例,建立连接transcriber = new SpeechTranscriber(client, getTranscriberListener());transcriber.setAppKey(appKey);//输入音频编码方式transcriber.setFormat(InputFormatEnum.PCM);//输入音频采样率transcriber.setSampleRate(SampleRateEnum.SAMPLE_RATE_16K);//是否返回中间识别结果transcriber.setEnableIntermediateResult(false);//是否生成并返回标点符号transcriber.setEnablePunctuation(true);//是否将返回结果规整化,比如将一百返回为100transcriber.setEnableITN(false);//此方法将以上参数设置序列化为json发送给服务端,并等待服务端确认transcriber.start();File file = new File(filepath);FileInputStream fis = new FileInputStream(file);byte[] b = new byte[3200];int len;while ((len = fis.read(b)) > 0) {logger.info("send data pack length: " + len);transcriber.send(b);// TODO  重要提示:这里是用读取本地文件的形式模拟实时获取语音流并发送的,因为read很快,所以这里需要sleep// TODO  如果是真正的实时获取语音,则无需sleep, 如果是8k采样率语音,第二个参数改为8000int deltaSleep = getSleepDelta(len, 16000);Thread.sleep(deltaSleep);}//通知服务端语音数据发送完毕,等待服务端处理完成long now = System.currentTimeMillis();logger.info("ASR wait for complete");transcriber.stop();logger.info("ASR latency : " + (System.currentTimeMillis() - now) + " ms");} catch (Exception e) {System.err.println(e.getMessage());} finally {if (null != transcriber) {transcriber.close();}}}public void shutdown() {client.shutdown();}public static void main(String[] args) throws Exception {String appKey = "填写你的appkey";String id = "填写你在阿里云网站上的AccessKeyId";String secret = "填写你在阿里云网站上的AccessKeySecret";String url = ""; // 默认即可,默认值:wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1if (args.length == 3) {appKey   = args[0];id       = args[1];secret   = args[2];} else if (args.length == 4) {appKey   = args[0];id       = args[1];secret   = args[2];url      = args[3];} else {System.err.println("run error, need params(url is optional): " + "<app-key> <AccessKeyId> <AccessKeySecret> [url]");System.exit(-1);}// TODO 重要提示: 这里用一个本地文件来模拟发送实时流数据,实际使用时,用户可以从某处实时采集或接收语音流并发送到ASR服务端String filepath = "nls-sample-16k.wav";SpeechTranscriberDemo demo = new SpeechTranscriberDemo(appKey, id, secret, url);demo.process(filepath);demo.shutdown();}
}

3.语音合成

1.Java SDK2.0

可从maven 服务器下载最新版本SDK:

<dependency>    <groupId>com.alibaba.nls</groupId>    <artifactId>nls-sdk-tts</artifactId>    <version>2.1.1</version>
</dependency>

1.服务验证

java -cp nls-example-tts-2.0.0-jar-with-dependencies.jar com.alibaba.nls.client.SpeechSynthesizerDemo

2.服务压测

java -jar nls-example-tts-2.0.0-jar-with-dependencies.jar

3.关键接口

  • NlsClient:语音处理client,相当于所有语音相关处理类的factory,全局创建一个实例即可.线程安全

  • SpeechSynthesizer:语音合成处理类,设置请求参数,发送请求.非线程安全

  • SpeechSynthesizerListener:语音合成监听类,监听返回结果.非线程安全.有如下两个抽象方法需要实现:

    /**   * 接收语音合成二进制数据   */  
    abstract public void onMessage(ByteBuffer message);  
    /**   * 语音合成结束事件通知   *   * @param response   */  
    abstract public void onComplete(SpeechSynthesizerResponse response);
    

4.SDK 调用注意事项

  1. NlsClient对象创建一次可以重复使用,每次创建消耗性能.NlsClient使用了netty的框架,创建时比较消耗时间和资源,但创建之后可以重复利用.建议调用程序将NlsClient的创建和关闭与程序本身的生命周期结合

  2. SpeechSynthesizer对象不能重复使用,一个语音合成任务对应一个SpeechSynthesizer对象.例如有N个文本需要语音合成,则要进行N次语音合成任务,创建N个SpeechSynthesizer对象

  3. 实现的SpeechSynthesizerListener对象和SpeechSynthesizer对象是一一对应的,不能将一个SpeechSynthesizerListener对象设置到多个SpeechSynthesizer对象中,否则不能区分是哪个语音合成任务

  4. Java SDK依赖了Netty网络库,版本需设置为4.1.17.Final及以上.如果您的应用中依赖了Netty,请确保版本符合要求

5.Java Demo

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import com.alibaba.nls.client.protocol.NlsClient;
import com.alibaba.nls.client.protocol.OutputFormatEnum;
import com.alibaba.nls.client.protocol.SampleRateEnum;
import com.alibaba.nls.client.protocol.tts.SpeechSynthesizer;
import com.alibaba.nls.client.protocol.tts.SpeechSynthesizerListener;
import com.alibaba.nls.client.protocol.tts.SpeechSynthesizerResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*** 此示例演示了*      语音合成API调用*      动态获取token*      流式合成TTS*      首包延迟计算* (仅作演示,需用户根据实际情况实现)*/
public class SpeechSynthesizerDemo {private static final Logger logger = LoggerFactory.getLogger(SpeechSynthesizerDemo.class);private static long startTime;private String appKey;NlsClient client;public SpeechSynthesizerDemo(String appKey, String accessKeyId, String accessKeySecret) {this.appKey = appKey;//TODO 重要提示 创建NlsClient实例,应用全局创建一个即可,生命周期可和整个应用保持一致,默认服务地址为阿里云线上服务地址//TODO 这里简单演示了获取token 的代码,该token会过期,实际使用时注意在accessToken.getExpireTime()过期前再次获取tokenAccessToken accessToken = new AccessToken(accessKeyId, accessKeySecret);try {accessToken.apply();System.out.println("get token: " + accessToken.getToken() + ", expire time: " + accessToken.getExpireTime());client = new NlsClient(accessToken.getToken());} catch (IOException e) {e.printStackTrace();}}public SpeechSynthesizerDemo(String appKey, String accessKeyId, String accessKeySecret, String url) {this.appKey = appKey;//TODO 重要提示 创建NlsClient实例,应用全局创建一个即可,生命周期可和整个应用保持一致,默认服务地址为阿里云线上服务地址//TODO 这里简单演示了获取token 的代码,该token会过期,实际使用时注意在accessToken.getExpireTime()过期前再次获取tokenAccessToken accessToken = new AccessToken(accessKeyId, accessKeySecret);try {accessToken.apply();System.out.println("get token: " + accessToken.getToken() + ", expire time: " + accessToken.getExpireTime());if(url.isEmpty()) {client = new NlsClient(accessToken.getToken());}else {client = new NlsClient(url, accessToken.getToken());}} catch (IOException e) {e.printStackTrace();}}private static SpeechSynthesizerListener getSynthesizerListener() {SpeechSynthesizerListener listener = null;try {listener = new SpeechSynthesizerListener() {File f=new File("tts_test.wav");FileOutputStream fout = new FileOutputStream(f);private boolean firstRecvBinary = true;//语音合成结束@Overridepublic void onComplete(SpeechSynthesizerResponse response) {// TODO 当onComplete时表示所有TTS数据已经接收完成,因此这个是整个合成延迟,该延迟可能较大,未必满足实时场景System.out.println("name: " + response.getName() +", status: " + response.getStatus()+", output file :"+f.getAbsolutePath());}//语音合成的语音二进制数据@Overridepublic void onMessage(ByteBuffer message) {try {if(firstRecvBinary) {// TODO 此处是计算首包语音流的延迟,收到第一包语音流时,即可以进行语音播放,以提升响应速度(特别是实时交互场景下)firstRecvBinary = false;long now = System.currentTimeMillis();logger.info("tts first latency : " + (now - SpeechSynthesizerDemo.startTime) + " ms");}byte[] bytesArray = new byte[message.remaining()];message.get(bytesArray, 0, bytesArray.length);fout.write(bytesArray);} catch (IOException e) {e.printStackTrace();}}@Overridepublic void onFail(SpeechSynthesizerResponse response){// TODO 重要提示: task_id很重要,是调用方和服务端通信的唯一ID标识,当遇到问题时,需要提供此task_id以便排查System.out.println("task_id: " + response.getTaskId() +//状态码 20000000 表示识别成功", status: " + response.getStatus() +//错误信息", status_text: " + response.getStatusText());}};} catch (Exception e) {e.printStackTrace();}return listener;}public void process() {SpeechSynthesizer synthesizer = null;try {//创建实例,建立连接synthesizer = new SpeechSynthesizer(client, getSynthesizerListener());synthesizer.setAppKey(appKey);//设置返回音频的编码格式synthesizer.setFormat(OutputFormatEnum.WAV);//设置返回音频的采样率synthesizer.setSampleRate(SampleRateEnum.SAMPLE_RATE_16K);//发音人synthesizer.setVoice("siyue");//语调,范围是-500~500,可选,默认是0synthesizer.setPitchRate(100);//语速,范围是-500~500,默认是0synthesizer.setSpeechRate(100);//设置用于语音合成的文本synthesizer.setText("欢迎使用阿里巴巴智能语音合成服务,您可以说北京明天天气怎么样啊");//此方法将以上参数设置序列化为json发送给服务端,并等待服务端确认long start = System.currentTimeMillis();synthesizer.start();logger.info("tts start latency " + (System.currentTimeMillis() - start) + " ms");SpeechSynthesizerDemo.startTime = System.currentTimeMillis();//等待语音合成结束synthesizer.waitForComplete();logger.info("tts stop latency " + (System.currentTimeMillis() - start) + " ms");} catch (Exception e) {e.printStackTrace();} finally {//关闭连接if (null != synthesizer) {synthesizer.close();}}}public void shutdown() {client.shutdown();}public static void main(String[] args) throws Exception {String appKey = "填写你的appkey";String id = "填写你在阿里云网站上的AccessKeyId";String secret = "填写你在阿里云网站上的AccessKeySecret";String url = ""; // 默认即可,默认值:wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1if (args.length == 3) {appKey   = args[0];id       = args[1];secret   = args[2];} else if (args.length == 4) {appKey   = args[0];id       = args[1];secret   = args[2];url      = args[3];} else {System.err.println("run error, need params(url is optional): " + "<app-key> <AccessKeyId> <AccessKeySecret> [url]");System.exit(-1);}SpeechSynthesizerDemo demo = new SpeechSynthesizerDemo(appKey, id, secret, url);demo.process();demo.shutdown();}
}

2.RESTful API2.0

1.功能介绍

语音合成RESTful API支持HTTPS GET和POST两种方法的请求,将待合成的文本上传到服务端,服务端返回文本的语音合成结果,开发者需要保证在语音合成结果返回之前连接不被中断

  • 支持设置合成音频的格式:pcm,wav,mp3

  • 支持设置合成音频的采样率:8000Hz、16000Hz

  • 支持设置多种发音人

  • 支持设置语速、语调、音量

    重要提示

  • 随着TTS合成效果的不断提升,算法的复杂度也越来越高,对用户而言,可能会遇到合成耗时变长的可能.因此我们建议您使用流式合成机制.本文档及SDK附带Demo示例中有相关流式处理示例代码可做参考

  • 单次调用传入文本不能超过300个字符,否则超过300字符的内容会被截断,只合成300字符以内的内容,对应更长文本的合成,可以参考SDK附带Demo中的长文本切分及拼接示例

2.服务地址

访问类型说明URLHost
外网访问所有服务器均可使用外网访问URLhttps://nls-gateway.cn-shanghai.aliyuncs.com/stream/v1/ttsnls-gateway.cn-shanghai.aliyuncs.com
阿里云上海ECS内网访问您使用阿里云上海ECS(即ECS地域为华东2(上海)),可使用内网访问URLhttp://nls-gateway.cn-shanghai-internal.aliyuncs.com/stream/v1/ttsnls-gateway.cn-shanghai-internal.aliyuncs.com

以下将以使用外网访问URL的方式进行介绍.如果您使用的是阿里云上海ECS,并想使用内网访问URL,则要使用HTTP协议,并替换外网访问的URL和Host

3.请求参数

语音合成需要设置的请求参数如下表所示.如果使用HTTPS GET方法的请求,需要将这些参数设置到HTTPS的URL请求参数中;如果使用HTTPS POST方法的请求,需要将这些参数设置到HTTPS的请求体(Body)中

名称类型是否必需描述
appkeyString应用appkey(获取方法请阅读创建项目一节)
textString待合成的文本,需要为UTF-8编码.使用GET方法,需要再采用RFC 3986规范进行urlencode编码,比如加号 + 编码为 %2B;使用POST方法不需要urlencode编码
tokenString服务鉴权Token,获取方法请阅读获取访问令牌一节.若不设置token参数,需要在HTTP Headers中设置X-NLS-Token字段来指定Token
formatString音频编码格式,支持的格式:pcm、wav、mp3,默认是pcm
sample_rateInteger音频采样率,支持16000Hz、8000Hz,默认是16000Hz
voiceString发音人,默认是xiaoyun,其他发音人名称请在简介中选择
volumeInteger音量,范围是0~100,默认50
speech_rateInteger语速,范围是-500~500,默认是0
pitch_rateInteger语调,范围是-500~500,可选,默认是0

4.GET方法上传文本

一个完整的语音合成RESTful API GET方法的请求包含以下要素:

1.URL

协议URL方法
HTTPShttps://nls-gateway.cn-shanghai.aliyuncs.com/stream/v1/ttsGET

2.请求参数

见上述请求参数表格.

如上URL和请求参数组成的完整请求链接如下所示,在浏览器中打开该链接可直接获取语音合成的结果:

# appkey请填入您的管控台创建的项目appkey,token请填入您的token,在浏览器中打开该链接,可直接获取语音合成结果.
# text的内容为"今天是周一,天气挺好的."
https://nls-gateway.cn-shanghai.aliyuncs.com/stream/v1/tts?appkey=${您的appkey}&token=${您的token}&text=%E4%BB%8A%E5%A4%A9%E6%98%AF%E5%91%A8%E4%B8%80%EF%BC%8C%E5%A4%A9%E6%B0%94%E6%8C%BA%E5%A5%BD%E7%9A%84%E3%80%82&format=wav&sample_rate=16000

3.HTTPS GET 请求头部

名称类型是否必需描述
X-NLS-TokenString服务鉴权Token,获取方法请阅读获取访问令牌一节.若请求参数中没有设置token参数,则需要在这里设置该字段

注意:

  • 服务鉴权Token参数既可以在请求参数token中设置,也可以在HTTPS Headers的X-NLS-Token字段设置,推荐使用请求参数token.

  • 参数text必须采用UTF-8编码,在采用RFC 3986规范进行urlencode编码,比如加号 + 编码为 %2B,星号 * 编码为 %2A,%7E 编码为 ~.

5.POST方法上传文本

一个完整的语音合成RESTful API POST请求包含以下要素:

1.URL

协议URL方法
HTTPShttps://nls-gateway.cn-shanghai.aliyuncs.com/stream/v1/ttsPOST

2.HTTPS POST 请求头部

名称类型是否必需描述
X-NLS-TokenString服务鉴权Token,获取方法请阅读获取访问令牌一节.若Body请求参数中没有设置token参数,则需要在这里设置该字段
Content-TypeString必须为“application/json”,表明HTTP Body的内容为JSON格式字符串
Content-LengthlongHTTP Body中内容的长度

3.HTTPS POST 请求体

HTTPS POST请求体传入的是请求参数组成的JSON格式的字符串,因此在HTTPS POST请求头部中的Content-Type必须设置为”application/json”.示例如下:

{    "appkey":"31f932fb",    "text":"今天是周一,天气挺好的.",    "token":"45034**********3c793",    "format":"wav"  }

注意:

  • 服务鉴权Token参数既可以在Body中的请求参数token中设置,也可以在HTTPS Headers的X-NLS-Token字段设置,推荐使用Body参数token.

  • 使用POST方法的请求,Body中的请求参数text必须采用UTF-8编码,但是不进行urlencode编码,注意与GET方法请求的区分.

6.响应结果

使用HTTPS GET方法和使用HTTPS POST方法请求的响应是相同的,响应的结果都包含在HTTPS的Body中.响应结果的成功或失败通过HTTPS Header的Content-Type字段来区分:

  • 成功响应

    • HTTPS Headers的Content-Type字段内容为audio/mpeg,表示合成成功,合成的语音数据在Body中.

    • HTTPS Header的X-NLS-RequestId字段内容为请求任务的task_id,方便调试排查.

    • Body内容为合成音频的二进制数据.

  • 失败响应

    • HTTPS Headers没有Content-Type字段,或者Content-Type字段内容为application/json,表示合成失败,错误信息在Body中.

    • HTTPS Header的X-NLS-RequestId字段内容为请求任务的task_id,方便调试排查.

    • Body内容为错误信息,JSON格式的字符串.如下所示:

{    "task_id":"8f95d0b9b6e948bc98e8d0ce64b0cf57",    "result":"",    "status":40000000,    "message":"Gateway:CLIENT_ERROR:in post data, json format illegal"
}

1.响应字段

失败响应时的错误信息字段如下表所示:

名称类型描述
task_idString32位请求任务ID,请记录该值,用于排查错误
resultString服务结果
statusInteger服务状态码
messageString服务状态描述

2.服务状态码

服务状态码服务状态描述解决办法
20000000请求成功 
40000000默认的客户端错误码查看错误消息或提交工单
40000001身份认证失败检查使用的令牌是否正确,是否过期
40000002无效的消息检查发送的消息是否符合要求
40000003无效的参数检查参数值设置是否合理
40000004空闲超时确认是否长时间没有发送数据掉服务端
40000005请求数量过多检查是否超过了并发连接数或者每秒钟请求数
50000000默认的服务端错误如果偶现可以忽略,重复出现请提交工单
50000001内部GRPC调用错误如果偶现可以忽略,重复出现请提交工单

7.Java Demo

import java.io.File;
import java.io.FileOutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import com.alibaba.fastjson.JSONObject;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class SpeechSynthesizerRestfulDemo {private String accessToken;private String appkey;public SpeechSynthesizerRestfulDemo(String appkey, String token) {this.appkey = appkey;this.accessToken = token;}/*** HTTPS GET 请求*/public void processGETRequet(String text, String audioSaveFile, String format, int sampleRate, String voice) {/*** 设置HTTPS GET请求* 1.使用HTTPS协议* 2.语音识别服务域名:nls-gateway.cn-shanghai.aliyuncs.com* 3.语音识别接口请求路径:/stream/v1/tts* 4.设置必须请求参数:appkey、token、text、format、sample_rate* 5.设置可选请求参数:voice、volume、speech_rate、pitch_rate*/String url = "https://nls-gateway.cn-shanghai.aliyuncs.com/stream/v1/tts";url = url + "?appkey=" + appkey;url = url + "&token=" + accessToken;url = url + "&text=" + text;url = url + "&format=" + format;url = url + "&voice=" + voice;url = url + "&sample_rate=" + String.valueOf(sampleRate);// voice 发音人,可选,默认是xiaoyun// url = url + "&voice=" + "xiaoyun";// volume 音量,范围是0~100,可选,默认50// url = url + "&volume=" + String.valueOf(50);// speech_rate 语速,范围是-500~500,可选,默认是0// url = url + "&speech_rate=" + String.valueOf(0);// pitch_rate 语调,范围是-500~500,可选,默认是0// url = url + "&pitch_rate=" + String.valueOf(0);System.out.println("URL: " + url);/*** 发送HTTPS GET请求,处理服务端的响应*/Request request = new Request.Builder().url(url).get().build();try {long start = System.currentTimeMillis();OkHttpClient client = new OkHttpClient();Response response = client.newCall(request).execute();System.out.println("total latency :" + (System.currentTimeMillis() - start) + " ms");System.out.println(response.headers().toString());String contentType = response.header("Content-Type");if ("audio/mpeg".equals(contentType)) {File f = new File(audioSaveFile);FileOutputStream fout = new FileOutputStream(f);fout.write(response.body().bytes());fout.close();System.out.println("The GET request succeed!");}else {// ContentType 为 null 或者为 "application/json"String errorMessage = response.body().string();System.out.println("The GET request failed: " + errorMessage);}response.close();} catch (Exception e) {e.printStackTrace();}}/*** HTTPS POST 请求*/public void processPOSTRequest(String text, String audioSaveFile, String format, int sampleRate, String voice) {/*** 设置HTTPS POST请求* 1.使用HTTPS协议* 2.语音合成服务域名:nls-gateway.cn-shanghai.aliyuncs.com* 3.语音合成接口请求路径:/stream/v1/tts* 4.设置必须请求参数:appkey、token、text、format、sample_rate* 5.设置可选请求参数:voice、volume、speech_rate、pitch_rate*/String url = "https://nls-gateway.cn-shanghai.aliyuncs.com/stream/v1/tts";JSONObject taskObject = new JSONObject();taskObject.put("appkey", appkey);taskObject.put("token", accessToken);taskObject.put("text", text);taskObject.put("format", format);taskObject.put("voice", voice);taskObject.put("sample_rate", sampleRate);// voice 发音人,可选,默认是xiaoyun// taskObject.put("voice", "xiaoyun");// volume 音量,范围是0~100,可选,默认50// taskObject.put("volume", 50);// speech_rate 语速,范围是-500~500,可选,默认是0// taskObject.put("speech_rate", 0);// pitch_rate 语调,范围是-500~500,可选,默认是0// taskObject.put("pitch_rate", 0);String bodyContent = taskObject.toJSONString();System.out.println("POST Body Content: " + bodyContent);RequestBody reqBody = RequestBody.create(MediaType.parse("application/json"), bodyContent);Request request = new Request.Builder().url(url).header("Content-Type", "application/json").post(reqBody).build();try {OkHttpClient client = new OkHttpClient();Response response = client.newCall(request).execute();String contentType = response.header("Content-Type");if ("audio/mpeg".equals(contentType)) {File f = new File(audioSaveFile);FileOutputStream fout = new FileOutputStream(f);fout.write(response.body().bytes());fout.close();System.out.println("The POST request succeed!");}else {// ContentType 为 null 或者为 "application/json"String errorMessage = response.body().string();System.out.println("The POST request failed: " + errorMessage);}response.close();} catch (Exception e) {e.printStackTrace();}}public static void main(String[] args) {if (args.length < 2) {System.err.println("SpeechSynthesizerRestfulDemo need params: <token> <app-key>");System.exit(-1);}String token = args[0];String appkey = args[1];SpeechSynthesizerRestfulDemo demo = new SpeechSynthesizerRestfulDemo(appkey, token);String text = "今天是周一,天气挺好的.";// 采用RFC 3986规范进行urlencode编码String textUrlEncode = text;try {textUrlEncode = URLEncoder.encode(textUrlEncode, "UTF-8").replace("+", "%20").replace("*", "%2A").replace("%7E", "~");} catch (UnsupportedEncodingException e) {e.printStackTrace();}System.out.println(textUrlEncode);String audioSaveFile = "syAudio.wav";String format = "wav";int sampleRate = 16000;demo.processGETRequet(textUrlEncode, audioSaveFile, format, sampleRate, "siyue");//demo.processPOSTRequest(text, audioSaveFile, format, sampleRate, "siyue");System.out.println("### Game Over ###");}
}

 

4.录音文件识别

1.Java SDK2.0

录音文件识别的Java Demo使用了阿里云Java SDK的CommonRequest用来提交录音文件识别请求和识别结果查询,采用的是RPC风格的POP API调用

<dependency>    <groupId>com.aliyun</groupId>    <artifactId>aliyun-java-sdk-core</artifactId>    <version>3.7.1</version>
</dependency>
<dependency>    <groupId>com.alibaba</groupId>    <artifactId>fastjson</artifactId>    <version>1.2.49</version>
</dependency>

1.阿里云鉴权client

使用过程中,所有的调用均通过阿里云账号来完成鉴权操作.通过传入阿里云账号的AccessKey ID和AccessKey Secret,调用阿里云Java SDK,得到client,示例如下:

final String accessKeyId = "您的AccessKey Id";
final String accessKeySecret = "您的AccessKey Secret";
/*** 地域ID*/
final String regionId = "cn-shanghai";
final String endpointName = "cn-shanghai";
final String product = "nls-filetrans";
final String domain = "filetrans.cn-shanghai.aliyuncs.com";
IAcsClient client;
// 设置endpoint
DefaultProfile.addEndpoint(endpointName, regionId, product, domain);
// 创建DefaultAcsClient实例并初始化
DefaultProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
client = new DefaultAcsClient(profile);

2.录音文件识别请求调用接口

Java Demo采用的是轮询的方式,提交录音文件识别请求,获取任务ID,供后续轮询使用

说明:只需设置JSON字符串中的参数,其他方法的参数值保持不变

/*** 创建CommonRequest 设置请求参数*/
CommonRequest postRequest = new CommonRequest();
postRequest.setDomain("filetrans.cn-shanghai.aliyuncs.com"); // 设置域名,固定值
postRequest.setVersion("2018-08-17");         // 设置API的版本号,固定值
postRequest.setAction("SubmitTask");          // 设置action,固定值
postRequest.setProduct("nls-filetrans");      // 设置产品名称,固定值
// 设置录音文件识别请求参数,以JSON字符串的格式设置到请求的Body中
JSONObject taskObject = new JSONObject();
taskObject.put("appkey", "您的appkey");    // 设置appkey,传入您管控台项目的appkey
taskObject.put("file_link", "您的录音文件访问链接");  // 设置录音文件访问链接,传入您需要识别的录音文件的链接
taskObject.put(KEY_VERSION, "4.0");  // 新接入请使用4.0版本,已接入(默认2.0)如需维持现状,请注释掉该参数设置
String task = taskObject.toJSONString();
postRequest.putBodyParameter("Task", task);  // 设置以上JSON字符串为Body参数
postRequest.setMethod(MethodType.POST);      // 设置为POST方式的请求
/*** 提交录音文件识别请求*/
String taskId = "";   // 获取录音文件识别请求任务的ID,以供识别结果查询使用
CommonResponse postResponse = client.getCommonResponse(postRequest);
if (postResponse.getHttpStatus() == 200) {JSONObject result = JSONObject.parseObject(postResponse.getData());String statusText = result.getString("StatusText");if ("SUCCESS".equals(statusText)) {System.out.println("录音文件识别请求成功响应: " + result.toJSONString());taskId = result.getString("TaskId");}else {System.out.println("录音文件识别请求失败: " + result.toJSONString());return;}
}
else {System.err.println("录音文件识别请求失败,Http错误码:" + postResponse.getHttpStatus());System.err.println("录音文件识别请求失败响应:" + JSONObject.toJSONString(postResponse));return;
}

3.录音文件识别结果查询

使用上面获得的任务ID,查询录音文件识别的结果

/*** 创建CommonRequest 设置任务ID*/
CommonRequest getRequest = new CommonRequest();
getRequest.setDomain("filetrans.cn-shanghai.aliyuncs.com");   // 设置域名,固定值
getRequest.setVersion("2018-08-17");             // 设置API版本,固定值
getRequest.setAction("GetTaskResult");           // 设置action,固定值
getRequest.setProduct("nls-filetrans");          // 设置产品名称,固定值
getRequest.putQueryParameter("TaskId", taskId);  // 设置任务ID为查询参数,传入任务ID
getRequest.setMethod(MethodType.GET);            // 设置为GET方式的请求
/*** 提交录音文件识别结果查询请求* 以轮询的方式进行识别结果的查询,直到服务端返回的状态描述为“SUCCESS”、“SUCCESS_WITH_NO_VALID_FRAGMENT”,或者为错误描述,则结束轮询.*/
String statusText = "";
while (true) {CommonResponse getResponse = client.getCommonResponse(getRequest);if (getResponse.getHttpStatus() != 200) {System.err.println("识别结果查询请求失败,Http错误码: " + getResponse.getHttpStatus());System.err.println("识别结果查询请求失败: " + getResponse.getData());break;}JSONObject result = JSONObject.parseObject(getResponse.getData());System.out.println("识别查询结果:" + result.toJSONString());statusText = result.getString("StatusText");if ("RUNNING".equals(statusText) || "QUEUEING".equals(statusText)) {// 继续轮询Thread.sleep(3000);}else {break;}
}
if ("SUCCESS".equals(statusText) || "SUCCESS_WITH_NO_VALID_FRAGMENT".equals(statusText)) {System.out.println("录音文件识别成功!");
}
else {System.err.println("录音文件识别失败!");
}

4.Java Demo

import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
public class FileTransJavaDemo {// 地域ID,常量内容,请勿改变public static final String REGIONID = "cn-shanghai";public static final String ENDPOINTNAME = "cn-shanghai";public static final String PRODUCT = "nls-filetrans";public static final String DOMAIN = "filetrans.cn-shanghai.aliyuncs.com";public static final String API_VERSION = "2018-08-17";public static final String POST_REQUEST_ACTION = "SubmitTask";public static final String GET_REQUEST_ACTION = "GetTaskResult";// 请求参数keypublic static final String KEY_APP_KEY = "appkey";public static final String KEY_FILE_LINK = "file_link";public static final String KEY_VERSION = "version";public static final String KEY_ENABLE_WORDS = "enable_words";// 响应参数keypublic static final String KEY_TASK = "Task";public static final String KEY_TASK_ID = "TaskId";public static final String KEY_STATUS_TEXT = "StatusText";public static final String KEY_RESULT = "Result";// 状态值public static final String STATUS_SUCCESS = "SUCCESS";private static final String STATUS_RUNNING = "RUNNING";private static final String STATUS_QUEUEING = "QUEUEING";// 阿里云鉴权clientIAcsClient client;public FileTransJavaDemo(String accessKeyId, String accessKeySecret) {// 设置endpointtry {DefaultProfile.addEndpoint(ENDPOINTNAME, REGIONID, PRODUCT, DOMAIN);} catch (ClientException e) {e.printStackTrace();}// 创建DefaultAcsClient实例并初始化DefaultProfile profile = DefaultProfile.getProfile(REGIONID, accessKeyId, accessKeySecret);this.client = new DefaultAcsClient(profile);}public String submitFileTransRequest(String appKey, String fileLink) {/*** 1. 创建CommonRequest 设置请求参数*/CommonRequest postRequest = new CommonRequest();// 设置域名postRequest.setDomain(DOMAIN);// 设置API的版本号,格式为YYYY-MM-DDpostRequest.setVersion(API_VERSION);// 设置actionpostRequest.setAction(POST_REQUEST_ACTION);// 设置产品名称postRequest.setProduct(PRODUCT);/*** 2. 设置录音文件识别请求参数,以JSON字符串的格式设置到请求的Body中*/JSONObject taskObject = new JSONObject();// 设置appkeytaskObject.put(KEY_APP_KEY, appKey);// 设置音频文件访问链接taskObject.put(KEY_FILE_LINK, fileLink);// 新接入请使用4.0版本,已接入(默认2.0)如需维持现状,请注释掉该参数设置taskObject.put(KEY_VERSION, "4.0");// 设置是否输出词信息,默认为false,开启时需要设置version为4.0及以上taskObject.put(KEY_ENABLE_WORDS, true);String task = taskObject.toJSONString();System.out.println(task);// 设置以上JSON字符串为Body参数postRequest.putBodyParameter(KEY_TASK, task);// 设置为POST方式的请求postRequest.setMethod(MethodType.POST);/*** 3. 提交录音文件识别请求,获取录音文件识别请求任务的ID,以供识别结果查询使用*/String taskId = null;try {CommonResponse postResponse = client.getCommonResponse(postRequest);System.err.println("提交录音文件识别请求的响应:" + postResponse.getData());if (postResponse.getHttpStatus() == 200) {JSONObject result = JSONObject.parseObject(postResponse.getData());String statusText = result.getString(KEY_STATUS_TEXT);if (STATUS_SUCCESS.equals(statusText)) {taskId = result.getString(KEY_TASK_ID);}}} catch (ClientException e) {e.printStackTrace();}return taskId;}public String getFileTransResult(String taskId) {/*** 1. 创建CommonRequest 设置任务ID*/CommonRequest getRequest = new CommonRequest();// 设置域名getRequest.setDomain(DOMAIN);// 设置API版本getRequest.setVersion(API_VERSION);// 设置actiongetRequest.setAction(GET_REQUEST_ACTION);// 设置产品名称getRequest.setProduct(PRODUCT);// 设置任务ID为查询参数getRequest.putQueryParameter(KEY_TASK_ID, taskId);// 设置为GET方式的请求getRequest.setMethod(MethodType.GET);/*** 2. 提交录音文件识别结果查询请求* 以轮询的方式进行识别结果的查询,直到服务端返回的状态描述为“SUCCESS”,或者为错误描述,则结束轮询.*/String result = null;while (true) {try {CommonResponse getResponse = client.getCommonResponse(getRequest);System.err.println("识别查询结果:" + getResponse.getData());if (getResponse.getHttpStatus() != 200) {break;}JSONObject rootObj = JSONObject.parseObject(getResponse.getData());String statusText = rootObj.getString(KEY_STATUS_TEXT);if (STATUS_RUNNING.equals(statusText) || STATUS_QUEUEING.equals(statusText)) {// 继续轮询,注意设置轮询时间间隔Thread.sleep(3000);}else {// 状态信息为成功,返回识别结果;状态信息为异常,返回空if (STATUS_SUCCESS.equals(statusText)) {result = rootObj.getString(KEY_RESULT);// 状态信息为成功,但没有识别结果,则可能是由于文件里全是静音、噪音等导致识别为空if(result == null) {result = "";}}break;}} catch (Exception e) {e.printStackTrace();}}return result;}public static void main(String args[]) throws Exception {if (args.length < 3) {System.err.println("FileTransJavaDemo need params: <AccessKey Id> <AccessKey Secret> <app-key>");}final String accessKeyId = args[0];final String accessKeySecret = args[1];final String appKey = args[2];String fileLink = "https://aliyun-nls.oss-cn-hangzhou.aliyuncs.com/asr/fileASR/examples/nls-sample-16k.wav";FileTransJavaDemo demo = new FileTransJavaDemo(accessKeyId, accessKeySecret);// 第一步:提交录音文件识别请求,获取任务ID用于后续的识别结果轮询String taskId = demo.submitFileTransRequest(appKey, fileLink);if (taskId != null) {System.out.println("录音文件识别请求成功,task_id: " + taskId);}else {System.out.println("录音文件识别请求失败!");return;}// 第二步:根据任务ID轮询识别结果String result = demo.getFileTransResult(taskId);if (result != null) {System.out.println("录音文件识别结果查询成功:" + result);}else {System.out.println("录音文件识别结果查询失败!");}}
}

补充说明:如果使用回调方式,请在task字符串中设置“enable_callback”、“callback_url”参数:

taskObject.put("enable_callback", true);
taskObject.put("callback_url", "回调地址");

回调服务示例:该服务用于回调方式获取转写结果,仅供参考,假设设置的回调地址是:http://ip:port/filetrans/callback/result

package com.example.filetrans;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@RequestMapping("/filetrans/callback")
@RestController
public class FiletransCallBack {// 以4开头的状态码是客户端错误private static final Pattern PATTERN_CLIENT_ERR = Pattern.compile("4105[0-9]*");// 以5开头的状态码是服务端错误private static final Pattern PATTERN_SERVER_ERR = Pattern.compile("5105[0-9]*");// 必须是post的方式@RequestMapping(value = "result", method = RequestMethod.POST)public void GetResult(HttpServletRequest request) {byte [] buffer = new byte[request.getContentLength()];ServletInputStream in = null;try {in = request.getInputStream();in.read(buffer, 0 ,request.getContentLength());in.close();// 获取json格式的文件转写结果String result = new String(buffer);JSONObject jsonResult = JSONObject.parseObject(result);// 解析并输出相关结果内容System.out.println("获取文件中转写回调结果:" + result);System.out.println("TaskId: " + jsonResult.getString("TaskId"));System.out.println("StatusCode: " + jsonResult.getString("StatusCode"));System.out.println("StatusText: " + jsonResult.getString("StatusText"));Matcher matcherClient = PATTERN_CLIENT_ERR.matcher(jsonResult.getString("StatusCode"));Matcher matcherServer = PATTERN_SERVER_ERR.matcher(jsonResult.getString("StatusCode"));// 以2开头状态码为正常状态码,回调方式方式正常状态只返回"21050000"if("21050000".equals(jsonResult.getString("StatusCode"))) {System.out.println("RequestTime: " + jsonResult.getString("RequestTime"));System.out.println("SolveTime: " + jsonResult.getString("SolveTime"));System.out.println("BizDuration: " + jsonResult.getString("BizDuration"));System.out.println("Result.Sentences.size: " +jsonResult.getJSONObject("Result").getJSONArray("Sentences").size());for (int i = 0; i < jsonResult.getJSONObject("Result").getJSONArray("Sentences").size(); i++) {System.out.println("Result.Sentences[" + i + "].BeginTime: " +jsonResult.getJSONObject("Result").getJSONArray("Sentences").getJSONObject(i).getString("BeginTime"));System.out.println("Result.Sentences[" + i + "].EndTime: " +jsonResult.getJSONObject("Result").getJSONArray("Sentences").getJSONObject(i).getString("EndTime"));System.out.println("Result.Sentences[" + i + "].SilenceDuration: " +jsonResult.getJSONObject("Result").getJSONArray("Sentences").getJSONObject(i).getString("SilenceDuration"));System.out.println("Result.Sentences[" + i + "].Text: " +jsonResult.getJSONObject("Result").getJSONArray("Sentences").getJSONObject(i).getString("Text"));System.out.println("Result.Sentences[" + i + "].ChannelId: " +jsonResult.getJSONObject("Result").getJSONArray("Sentences").getJSONObject(i).getString("ChannelId"));System.out.println("Result.Sentences[" + i + "].SpeechRate: " +jsonResult.getJSONObject("Result").getJSONArray("Sentences").getJSONObject(i).getString("SpeechRate"));System.out.println("Result.Sentences[" + i + "].EmotionValue: " +jsonResult.getJSONObject("Result").getJSONArray("Sentences").getJSONObject(i).getString("EmotionValue"));}}else if(matcherClient.matches()) {System.out.println("状态码以4开头表示客户端错误......");}else if(matcherServer.matches()) {System.out.println("状态码以5开头表示服务端错误......");}else {}} catch (IOException e) {e.printStackTrace();}}
}
查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. 英特尔AI医疗实战曝光:10倍加速辅助诊断、准确度高达90%

    原文链接:https://www.toutiao.com/i6763135378628018691/2019-11-25 15:01:23机器之心原创作者:力琴深耕医疗健康领域 20 年,医疗健康数字化、药物治疗精确化一直是英特尔的重要议题。每年都有 1800 万人因心血管疾病失去生命,易患疾病排名前三,又称头号健康杀手。在医院…...

    2024/3/11 16:16:40
  2. SAP QA32试图做UD,系统报错-工厂 BTYC中的 QM 基选设置需要维护

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/weixin_42137700/article/details/103271009SAP QA32 试图做UD,系统报错 - 工厂 BTYC 中的 QM 基选设置需要维护 -检验批 …...

    2024/3/18 7:57:52
  3. static和this关键字

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/qq_44682003/article/details/103289644讲之前给大家多讲下:java类中的构造方法 构造方法的结构: [修饰符列表] 构造方法…...

    2024/3/14 17:58:02
  4. 超时时杀死python subprocess子进程?

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/zhajio/article/details/103289477import subprocess as sub import threadingclass RunCmd(threading.Thread):def __init…...

    2024/3/13 23:41:27
  5. CentOS 7 搭建frp内网穿透

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/weixin_44567104/article/details/103277641frp内网穿透是通过一个带有公网IP的服务器进行中转,对被控主机实现反向代理,…...

    2024/3/7 7:57:40
  6. 2019年18家大厂Java面试题整理了350道(分布式+微服务+高并发+性能调优+框架源码)

    原文链接:https://blog.csdn.net/Design407/article/details/102963498前言 2019年还有不到2个月的时间就结束了,这一你,你收获了多少?前段时间一直有粉丝问我,有没有今年一些大厂Java面试题总结?最新抽时间整理了一些,分享给大家,大家一起共享学习!一、性能调优系列 …...

    2024/3/7 7:57:56
  7. android.view.InflateException: Binary XML file line #

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/ai1362425349/article/details/103288586#后面跟着的是错误的行数,一般用于定位错误的位置,然后找到错误的xml,进入文件…...

    2024/3/7 7:58:05
  8. javaFX学习之安装JavaFX Scene Builder 2.0

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/YCJ_xiyang/article/details/101423730目录一 下载二 idea配置JavaFX Scene Builder 是一个可视化布局工具,可快速设计 Ja…...

    2024/3/7 7:58:10
  9. 曾经的网易,因为“养猪”,名噪一时,“暴力裁员”却将网易推上风口浪尖

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/power_360/article/details/10328876811月23日,一篇题《网易裁员,让保安把身患绝症的我赶出公司。我在网易亲身经历的噩梦…...

    2024/3/7 7:58:17
  10. C++VS2019中新建自定义模板与删除自定义模板

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/weixin_41498246/article/details/103287795如果经常项目需要用到相同的一些代码,如头文件,命名空间等,可以考虑创建一个…...

    2024/3/19 8:43:36
  11. NYIST19级-搜索小练:A题

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/qq_45740533/article/details/103284525题目链接: https://vjudge.net/contest/345248#problem/A 题目翻译: GeoSurvComp…...

    2024/3/14 13:24:14
  12. 代码片段:自己动手写SQL慢查询统计

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/u012534326/article/details/103288676前言 首先,MySQL是有自己的慢查询日志记录的,但是作为开发者,并不一定有权限查看MyS…...

    2024/3/14 17:13:14
  13. Vue打包发布问题总汇

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/m0_38081413/article/details/103287882** Vue打包发布问题总汇 ** 一、css样式局部样式和全局样式影响 现象:在开发环境中…...

    2024/3/16 13:58:54
  14. 为seo而生的WordPress主题RabbitV3.0主题分享

    WordPress主题Rabbit V3.0主题.rarRabbit v3.0主题为SEO而生,是一款专注于SEO优化用途的WordPress主题,专为博客、自媒体、资讯类等类型网站SEO优化设计开发,自适应兼容手机、平板设备,支持前端用户中心,可以前端发布/投稿文章,同时主题支持专题功能,可以添加文章专题。…...

    2024/3/19 1:07:47
  15. [NLP笔记] 分词

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/github_35736728/article/details/103288058文章目录分词工具 分词工具 ref: 自然语言处理相关项目列表(2018/02/05更新)Fo…...

    2024/3/9 3:05:06
  16. 代理模式(静态代理和动态代理)(为理解aop做准备)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/weixin_43980559/article/details/103285722为什么要学习代理模式 因为aop的底层机制就是动态代理 代理模式静态代理 动态代…...

    2024/3/18 8:21:27
  17. roscore和conda都打不开

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/qq_36013249/article/details/103287573#ros系统仿真#起初是运行节点出现了libuuid.so.1: no version information availab…...

    2024/3/7 7:59:10
  18. 交叉编译zint并在qt中显示条码

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/zmlovelx/article/details/103287820下载源码 https://sourceforge.net/projects/zint/ tar -xvf zint-2.6.7.tar.gz cd zi…...

    2024/3/12 6:50:38
  19. 创建有模式对话框

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/ccnu027cs/article/details/103279888使用有模式对话框时在对话框弹出后调用函数不会立即返回,而是等到对话框销毁后才会返…...

    2024/3/8 20:50:11
  20. VS2015 远程调试方法

    原文链接:https://blog.csdn.net/zhouschina/article/details/77513235在没有安装VS2015的机器上安装发布的程序,运行出现异常,而开发机器上不会出现异常。因此采用远程调试。下面介绍启动远程调试的方法。 第一步:拷贝C:\Program Files (x86)\Microsoft Visual Studio 14.…...

    2024/3/13 15:55:27

最新文章

  1. js教程(7)

    一、事件监听&#xff08;事件绑定&#xff09; 1.事件 事件是在编程时系统内发生的动作或者发生的事情&#xff0c;比如用户在网页上点击按钮&#xff0c;摁下键盘的某个键。 2.事件监听 事件监听就是让程序检测是否有事件产生&#xff0c;一旦有事件触发&#xff0c;就立即…...

    2024/3/19 10:03:49
  2. Python Web开发记录 Day13:Django part7 Ajax入门与案例(任务管理)

    名人说&#xff1a;莫道桑榆晚&#xff0c;为霞尚满天。——刘禹锡&#xff08;刘梦得&#xff0c;诗豪&#xff09; 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 1、Ajax入门①简介②工作原理③优点④缺点⑤使用…...

    2024/3/19 10:02:30
  3. 旋转花键的制造工艺

    旋转花键的制造工艺是一门精细的技术&#xff0c;涉及多个步骤和精细的操作&#xff0c;以确保最终产品的质量和性能&#xff0c;下面简单介绍下旋转花键的制造工艺。 1、原材料准备&#xff1a;制造旋转花键的核心是选择合适的材料&#xff0c;根据花键的规格和性能要求&#…...

    2024/3/19 9:30:10
  4. 3.4 bp,si,di寄存器,寻址方式,寄存器总结

    汇编语言 1. [bxidata] 我们可以用[bx]来指明一个内存单元我们也可以用[bxidata]来表示一个内存单元&#xff0c;它的偏移地址为bx中的数值加上idata mount c d:masm c: debug r d 2000:1000 e 2000:1000 12 34 56 78 a mov ax,2000 mov ds,ax mov bx,1000 mov ax,[bx] mov c…...

    2024/3/19 8:44:55
  5. 《Learning Hierarchical Modular Networks for Video Captioning》论文笔记

    论文信息 原文链接&#xff1a; Learning Hierarchical Modular Networks for Video Captioning | IEEE Journals & Magazine | IEEE Xplore 原文代码 GitHub - MarcusNerva/HMN: [CVPR2022] Official code for Hierarchical Modular Network for Video Captioning. Ou…...

    2024/3/18 16:40:30
  6. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/3/19 4:35:35
  7. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/3/19 4:35:32
  8. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/3/18 12:12:47
  9. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/3/19 8:35:19
  10. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/3/18 17:59:32
  11. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/3/19 8:34:23
  12. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/3/18 11:23:08
  13. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/3/19 1:14:48
  14. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/3/18 18:26:16
  15. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/3/19 4:35:25
  16. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/3/18 17:51:01
  17. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/3/19 7:49:55
  18. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/3/19 4:35:22
  19. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/3/19 1:10:16
  20. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/3/19 3:31:29
  21. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/3/18 23:14:29
  22. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/3/19 8:34:41
  23. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/3/19 4:35:16
  24. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/3/19 4:35:15
  25. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/3/19 4:35:14
  26. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  27. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  28. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  29. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  30. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  31. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  32. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  33. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  34. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  35. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  36. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  37. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  38. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  39. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  40. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  41. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  42. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  43. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  44. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  45. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57