短语音识别:将一段不超过60秒的语音转换成文本,将音频文件中的文字信息识别出来,代替传统输入场景。
Hi,您好,欢迎使用有道智云短语音识别API接口服务。
如果您想快速体验服务,建议您前往 语音识别体验中心 或者在体验中心右下侧找到小程序二维码,扫描进行体验。
本文档主要针对需要集成API的技术开发工程师,详细描述短语音识别能力相关的技术内容。
如果您有与我们商务合作的需求,可以通过以下方式联系我们:
商务邮箱: AIcloud_Business@corp.youdao.com
如果您对文档内容有任何疑问,可以通过以下几种方式联系我们:
客服QQ:1906538062
智云语音技术交流QQ群:861723255
联系邮箱: zyservice@corp.youdao.com
温馨提示:
应用ID
和应用密钥
;如果您还没有,请按照 新手指南 获取。有道短语音识别API接口提供有道的短语音识别服务,包含了中文和英文的识别功能。您只需要通过调用有道语音识别API,传入待识别的音频文件,并指定要识别的源语言种类,以POST方式请求就可以得到相应的识别结果。
有道语音识别API HTTPS地址:
https://openapi.youdao.com/asrapi
语音支持:
格式支持:wav(不压缩,pcm编码,采样率:推荐16k ,编码:16bit位深的单声道),aac,mp3
格式 | 代码 |
---|---|
wav | wav |
aac | aac |
mp3 | mp3 |
注:上传的文件时长不能超过60s,文件大小不能超过10M。
调用API需要向接口发送以下字段来访问服务。
字段名 | 类型 | 含义 | 必填 | 备注 |
---|---|---|---|---|
q | text | 要翻译的音频文件的Base64编码字符串 | True | 必须是Base64编码 |
langType | text | 源语言 | True | 支持语言 |
appKey | text | 应用 ID | True | 可在 应用管理 查看 |
salt | text | UUID | True | uuid,唯一通用识别码 |
curtime | text | 时间戳(秒) | true | 秒数 |
sign | text | 签名,通过sha256(应用ID+q+salt+curtime+密钥)生成 | True | sha256(应用ID+input+salt+curtime+密钥) |
signType | text | 签名版本 | True | v3 |
format | text | 语音文件的格式,wav | true | wav |
rate | text | 采样率, 推荐 16000 采用率 | true | 16000 |
channel | text | 声道数, 仅支持单声道,请填写固定值1 | true | 1 |
type | text | 上传类型, 仅支持base64上传,请填写固定值1 | true | 1 |
签名
sign
生成方法如下:
1、将请求参数中的应用ID "appKey"
,Base64编码字符串 "q"
,UUID "salt"
,时间戳 "curTime"
和应用密钥
按照应用ID+q+salt+curTime+应用密钥
的顺序拼接得到字符串str
。其中,q的计算方式为:
q
=q前10个字符
+q长度
+q后10个字符
(当q长度大于20)或input
=q字符串
(当q长度小于等于20);
注意:
应用ID+q+salt+curTime+密钥
字符串时,q
不需要做 URL encode,在生成签名之后,发送 HTTP 请求之前才需要对要发送的待翻译文本字段 q
做 URL encode。响应结果是以json形式输出,包含字段如下表所示:
字段 | 含义 |
---|---|
errorCode | 识别结果错误码,一定存在。 详细信息参加 错误代码列表 |
result | 识别结果,识别成功一定存在 |
{
"result": [
"今天天气不错" //识别结果
],
"errorCode": "0", //错误码。一定存在
}
英文名 | 中文名 | 代码 |
---|---|---|
Arabic | 阿拉伯语 | ar |
Bahasa (Indonesia) | 巴哈萨语(印度尼西亚) | in |
Cantonese | 粤语 | yue |
Catalan | 加泰隆语 | ca |
Czech | 捷克语 | cs |
Danish | 丹麦语 | da |
Dutch | 荷兰语 | nl |
Dutch (Belgium) | 荷兰语(比利时) | nl-BEL |
English (Australia) | 英语(澳大利亚) | en-AUS |
English (GB) | 英语(英国) | en-GBR |
English (India) | 英语(印度) | en-IND |
English (Ireland) | 英语(爱尔兰) | en-IRL |
English (Scotland) | 英语(苏格兰) | en-SCT |
English (South Africa) | 英语(南非) | en-ZAF |
English (US) | 英语(美国) | en |
Finnish | 芬兰语 | fi |
French | 法语 | fr |
French (Canada) | 法语(加拿大) | fr-CAN |
German | 德语 | de |
Greek | 希腊语 | el |
Hebrew | 希伯来语 | he |
Hindi | 印地语 | hi |
Hungarian | 匈牙利语 | hu |
Italian | 意大利语 | it |
Japanese | 日语 | ja |
Korean | 韩语 | ko |
Mandarin (China) | 普通话(中国) | zh-CHS |
Mandarin (Taiwan) | 普通话(中国台湾) | zh-TWN |
Norwegian | 挪威语 | no |
Polish | 波兰语 | pl |
Portuguese (Brazil) | 葡萄牙语(巴西) | pt-BRA |
Portuguese (Portugal) | 葡萄牙语(葡萄牙) | pt |
Romanian | 罗马尼亚语 | ro |
Russian | 俄语 | ru |
Slovak | 斯洛伐克语 | sk |
Spanish (Castilian) | 西班牙语(卡斯蒂利亚) | es-ESP |
Spanish (Columbia) | 西班牙语(哥伦比亚) | es-COL |
Spanish (Mexico) | 西班牙语(墨西哥) | es-MEX |
Spanish (Mexico) | 西班牙语 | es |
Swedish | 瑞典语 | sv |
Thai | 泰语 | th |
Turkish | 土耳其语 | tr |
支持格式 | 音频大小上限 | 单次最大请求时长(s) | 每小时最大查询次数 | 每小时最大音频时长(s) | 支持语言 |
---|---|---|---|---|---|
wav | 10M(编码后) | 60 | 3000 | 60000 | 中/英/日/韩等详细可参考支持的语言列表 |
错误码 | 含义 |
---|---|
101 | 缺少必填的参数,首先确保必填参数齐全,然后,确认参数书写是否正确。 |
102 | 不支持的语言类型 |
103 | 翻译文本过长 |
104 | 不支持的API类型 |
105 | 不支持的签名类型 |
106 | 不支持的响应类型 |
107 | 不支持的传输加密类型 |
108 | 应用ID无效,注册账号,登录后台创建应用并完成绑定,可获得应用ID和应用密钥等信息 |
109 | batchLog格式不正确 |
110 | 无相关服务的有效应用,应用没有绑定服务,可以新建服务。注:某些服务的结果发音需要tts服务,需要在控制台创建语音合成实例绑定应用后方能使用。 |
111 | 开发者账号无效 |
112 | 请求服务无效 |
113 | q不能为空 |
114 | 不支持的图片传输方式 |
201 | 解密失败,可能为DES,BASE64,URLDecode的错误 |
202 | 签名检验失败,如果确认应用ID和应用密钥的正确性,仍返回202,一般是编码问题。请确保翻译文本 q 为UTF-8编码. |
203 | 访问IP地址不在可访问IP列表 |
205 | 请求的接口与应用的平台类型不一致,确保接入方式(Android SDK、IOS SDK、API)与创建的应用平台类型一致。如有疑问请参考入门指南 |
206 | 因为时间戳无效导致签名校验失败 |
207 | 重放请求 |
301 | 辞典查询失败 |
302 | 翻译查询失败 |
303 | 服务端的其它异常 |
304 | 会话闲置太久超时 |
401 | 账户已经欠费停 |
402 | offlinesdk不可用 |
411 | 访问频率受限,请稍后访问 |
412 | 长请求过于频繁,请稍后访问 |
1001 | 无效的OCR类型 |
1002 | 不支持的OCR image类型 |
1003 | 不支持的OCR Language类型 |
1004 | 识别图片过大 |
1201 | 图片base64解密失败 |
1301 | OCR段落识别失败 |
1411 | 访问频率受限 |
1412 | 超过最大识别字节数 |
2003 | 不支持的语言识别Language类型 |
2004 | 合成字符过长 |
2005 | 不支持的音频文件类型 |
2006 | 不支持的发音类型 |
2201 | 解密失败 |
2301 | 服务的异常 |
2411 | 访问频率受限,请稍后访问 |
2412 | 超过最大请求字符数 |
3001 | 不支持的语音格式 |
3002 | 不支持的语音采样率 |
3003 | 不支持的语音声道 |
3004 | 不支持的语音上传类型 |
3005 | 不支持的语言类型 |
3006 | 不支持的识别类型 |
3007 | 识别音频文件过大 |
3008 | 识别音频时长过长 |
3009 | 不支持的音频文件类型 |
3010 | 不支持的发音类型 |
3201 | 解密失败 |
3301 | 语音识别失败 |
3302 | 语音翻译失败 |
3303 | 服务的异常 |
3411 | 访问频率受限,请稍后访问 |
3412 | 超过最大请求字符数 |
4001 | 不支持的语音识别格式 |
4002 | 不支持的语音识别采样率 |
4003 | 不支持的语音识别声道 |
4004 | 不支持的语音上传类型 |
4005 | 不支持的语言类型 |
4006 | 识别音频文件过大 |
4007 | 识别音频时长过长 |
4201 | 解密失败 |
4301 | 语音识别失败 |
4303 | 服务的异常 |
4304 | 识别结果为空 |
4411 | 访问频率受限,请稍后访问 |
4412 | 超过最大请求时长 |
4416 | 包含不合时宜词汇 |
4414 | 音频格式转换失败 |
5001 | 无效的OCR类型 |
5002 | 不支持的OCR image类型 |
5003 | 不支持的语言类型 |
5004 | 识别图片过大 |
5005 | 不支持的图片类型 |
5006 | 文件为空 |
5201 | 解密错误,图片base64解密失败 |
5301 | OCR段落识别失败 |
5411 | 访问频率受限 |
5412 | 超过最大识别流量 |
9001 | 不支持的语音格式 |
9002 | 不支持的语音采样率 |
9003 | 不支持的语音声道 |
9004 | 不支持的语音上传类型 |
9005 | 不支持的语音识别 Language类型 |
9301 | ASR识别失败 |
9303 | 服务器内部错误 |
9411 | 访问频率受限(超过最大调用次数) |
9412 | 超过最大处理语音长度 |
10001 | 无效的OCR类型 |
10002 | 不支持的OCR image类型 |
10004 | 识别图片过大 |
10201 | 图片base64解密失败 |
10301 | OCR段落识别失败 |
10411 | 访问频率受限 |
10412 | 超过最大识别流量 |
11001 | 不支持的语音识别格式 |
11002 | 不支持的语音识别采样率 |
11003 | 不支持的语音识别声道 |
11004 | 不支持的语音上传类型 |
11005 | 不支持的语言类型 |
11006 | 识别音频文件过大 |
11007 | 识别音频时长过长,最大支持30s |
11201 | 解密失败 |
11301 | 语音识别失败 |
11303 | 服务的异常 |
11411 | 访问频率受限,请稍后访问 |
11412 | 超过最大请求时长 |
12001 | 图片尺寸过大 |
12002 | 图片base64解密失败 |
12003 | 引擎服务器返回错误 |
12004 | 图片为空 |
12005 | 不支持的识别图片类型 |
12006 | 图片无匹配结果 |
13001 | 不支持的角度类型 |
13002 | 不支持的文件类型 |
13003 | 表格识别图片过大 |
13004 | 文件为空 |
13301 | 表格识别失败 |
15001 | 需要图片 |
15002 | 图片过大(1M) |
15003 | 服务调用失败 |
17001 | 需要图片 |
17002 | 图片过大(1M) |
17003 | 识别类型未找到 |
17004 | 不支持的识别类型 |
17005 | 服务调用失败 |
本部分描述如何把其他格式的音频转成符合语音识别输入要求的格式文件。
语音识别底层使用的是wav格式,因此推荐使用wav格式音频。音频格式转换推荐使用ffmpeg
ffmpeg是一个自由软件,可以运行音频、视频多种格式的录影、转换、流功能,包含libavcodec--这是一个用于多个项目中的音频、视频的解码器库,以及libavformat--一个音频和视频格式转换库。
ffmpeg官网:https://www.ffmpeg.org/
ffmpeg的github地址:https://github.com/FFmpeg/FFmpeg
ffmpeg默认支持pcm与wav(pcm编码)格式,额外的编译参数如下:
—enable-libopencore-amrnb 支持amr-nb(8000 采样率) 读写
—enable-libopencore-amrwb 支持amr-wb(16000 采样率) 读取
—enable-libvo-amrwbenc 支持amr-wb(16000 采样率) 写入
—enable-libmp3lame 支持mp3 写入
ffmpeg -codecs 可以查看所有的格式:
D..... = Decoding supported # 读取
.E.... = Encoding supported # 写入
..A... = Audio codec # 音频编码
....L. = Lossy compression # 有损
.....S = Lossless compression # 无损
DEA..S pcm_s16le PCM signed 16-bit little-endian
DEA.LS wavpack WavPack
DEA.L. mp3 MP3 (MPEG audio layer 3)
DEA.L. amr_nb AMR-NB (Adaptive Multi-Rate NarrowBand)
DEA.L. amr_wb AMR-WB (Adaptive Multi-Rate WideBand)
-i 设定输入流
-f 设定格式
-ss 开始时间
wav、mp3、amr格式都自带头部,包含采样率、编码、多声道等信息。而pcm为原始音频信息,没有头部信息。wav(pcm编码)就是pcm文件加了wav的头部信息。
输入wav、mp3、amr:
ffmpeg -i audio.wav/audio.mp3/audio.amr
输入pcm格式:pcm需要额外告知编码格式,采样率,单声道信息
-f s16le -ac 1 -ar 16000 -i 16k.pcm //单声道、16000采样率、16bits编码的pcm文件
-ar 设定采样率
-ac 设定声音的channel数
-acodec 设定声音编解码器,未设定时则使用与输入流相同的编解码器
-an 不处理音频
-ab 设置比特率(单位:bit/s,也许老版是kb/s),前面ac设置为立体声时要以一半的比特率来设置,比如192kbps的就设置为96,转换默认比特率都较小,转换默认比特率都较小,要听到高品质声音的话建议设到160kbps(80)以上。
在原始采样率 大于或者接近16000的时候,推荐使用16000,8000采样率会降低识别效果。
输出wav和amr时,如果没有指定声音编解码器,则会选择默认的编码器。
输出pcm:
-f s16le -ac 1 -ar 16000 16k.pcm // 单声道 16000 采样率 16bits编码 pcm文件
输出wav:
-ac 1 -ar 16000 16k.wav //单声道 16000 采样率 16bits编码 pcm编码的wav文件
amr-nb:全称是:Adaptive Multi-Rate,自适应多速率,是一种音频编码文件格式,专用于有效地压缩语音频率。amr-nb格式只能选 8000采样率。bit rates越高音质越好,但是文件越大
bit rates 4.75k, 5.15k, 5.9k, 6.7k, 7.4k, 7.95k, 10.2k or 12.2k
输出 amr-wb 格式,采样率 16000。 bit rates越高音质越好,但是文件越大。
6600 8850 12650 14250 15850 18250 19850 23050 23850
-y 覆盖同名文件
-v 日志输出 基本如-v ERROR -v quiet等
ffmpeg {常用参数} {输入音频参数} {输出音频参数}
wav 文件转 16k 16bits 位深的单声道pcm文件
ffmpeg -y -i 16k.wav -acodec pcm_s16le -f s16le -ac 1 -ar 16000 16k.pcm
pcm文件转wav:
ffmpeg -y -f s16le -ar 16000 -ac 1 -acodec pcm_s16le 16k.pcm 16k.wav
m4a文件转16k 16bits 位深的单声道pcm文件
D:\ffmpeg\bin>ffmpeg -y -i test.m4a -acodec pcm_s16le -f s16le -ac 1 -ar 1600
0 16k.pcm
pcm保存的是未压缩的音频信息,没有头文件
16bits编码是指每次采样信息用2个字节保存。
16000采样率,是指1秒采样16000次,常见的音频是44100HZ,即一秒采样44100次。
单声道: 只有一个声道。
根据这些信息,可以得出:
1ms的16采样率音频文件大小是 2*16 = 32字节 。
1ms的8采样率音频文件大小是 2*8 = 16字节,由此即可得到音频的长度。
上线日期 | 版本号 | 更新内容 |
---|---|---|
2018.03.29 | v1.0.0 | 支持15s以内音频识别;支持中文或者英文音频识别。 |
2019.05.29 | v1.0.0 | 支持2min以内音频识别;支持中英混合音频识别。 |
package com.youdao.ai;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;
import java.util.Base64;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class AsrV2Demo {
private static final String YOUDAO_URL = "https://openapi.youdao.com/asrapi";
private static final String APP_KEY = "您的应用ID";
private static final String APP_SECRET = "您的应用密钥";
public static String truncate(String q) {
if (q == null) {
return null;
}
int len = q.length();
String result;
return len <= 20 ? q : (q.substring(0, 10) + len + q.substring(len - 10, len));
}
public static String loadAsBase64(String filename) {
InputStream in = null;
byte[] data = null;
try {
in = new FileInputStream(filename);
data = new byte[in.available()];
in.read(data);
in.close();
} catch (IOException e) {
e.printStackTrace();
}
return Base64.getEncoder().encodeToString(data);
}
public static String doRequest(String url, Map<String,String> requestParams) throws Exception{
String result = null;
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(url);
List<NameValuePair> params = new ArrayList<NameValuePair>();
for (String key : requestParams.keySet()) {
params.add(new BasicNameValuePair(key, requestParams.get(key)));
}
httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
try {
HttpEntity httpEntity = httpResponse.getEntity();
result = EntityUtils.toString(httpEntity, "utf-8");
EntityUtils.consume(httpEntity);
} finally {
try {
if (httpResponse != null) {
httpResponse.close();
}
} catch(IOException e) {
System.out.println("## release resouce error ##" + e);
}
}
return result;
}
public static String getDigest(String string) {
if (string == null) {
return null;
}
char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
byte[] btInput = string.getBytes(StandardCharsets.UTF_8);
try {
MessageDigest mdInst = MessageDigest.getInstance("SHA-256");
mdInst.update(btInput);
byte[] md = mdInst.digest();
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (byte byte0 : md) {
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (NoSuchAlgorithmException e) {
return null;
}
}
public static void main(String[] args) throws Exception {
Map<String, String> params = new HashMap<String, String>();
String filename = "音频的路径";
String langType = "合成文本的语言类型";
params.put("appKey", APP_KEY);
String q = loadAsBase64(filename);
params.put("q", q);
params.put("format", "wav");
params.put("rate", "16000");
params.put("channel", "1");
params.put("docType", "json");
params.put("type", "1");
String salt = UUID.randomUUID().toString();
params.put("salt", salt);
params.put("langType", langType);
String curtime = String.valueOf(System.currentTimeMillis() / 1000);
params.put("curtime", curtime);
String signStr = APP_KEY + truncate(q) + salt + curtime + APP_SECRET;
String sign = getDigest(signStr);
params.put("sign", sign);
params.put("signType", "v2");
String result = doRequest(YOUDAO_URL, params);
System.out.println(result);
}
}
# -*- coding: utf-8 -*-
import sys
import uuid
import requests
import wave
import base64
import hashlib
import time
reload(sys)
sys.setdefaultencoding('utf-8')
YOUDAO_URL = 'https://openapi.youdao.com/asrapi'
APP_KEY = '您的应用ID'
APP_SECRET = '您的应用密钥'
def truncate(q):
if q is None:
return None
q_utf8 = q.decode("utf-8")
size = len(q_utf8)
return q_utf8 if size <= 20 else q_utf8[0:10] + str(size) + q_utf8[size - 10:size]
def encrypt(signStr):
hash_algorithm = hashlib.sha256()
hash_algorithm.update(signStr.encode('utf-8'))
return hash_algorithm.hexdigest()
def do_request(data):
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
return requests.post(YOUDAO_URL, data=data, headers=headers)
def connect():
audio_file_path = '音频的路径'
lang_type = '合成文本的语言类型'
extension = audio_file_path[audio_file_path.rindex('.')+1:]
if extension != 'wav':
print '不支持的音频类型'
sys.exit(1)
wav_info = wave.open(audio_file_path, 'rb')
sample_rate = wav_info.getframerate()
nchannels = wav_info.getnchannels()
wav_info.close()
with open(audio_file_path, 'rb') as file_wav:
q = base64.b64encode(file_wav.read())
data = {}
curtime = str(int(time.time()))
data['curtime'] = curtime
salt = str(uuid.uuid1())
signStr = APP_KEY + truncate(q) + salt + curtime + APP_SECRET
sign = encrypt(signStr)
data['appKey'] = APP_KEY
data['q'] = q
data['salt'] = salt
data['sign'] = sign
data['signType'] = "v2"
data['langType'] = lang_type
data['rate'] = sample_rate
data['format'] = 'wav'
data['channel'] = nchannels
data['type'] = 1
response = do_request(data)
print response.content
if __name__ == '__main__':
connect()
# -*- coding: utf-8 -*-
import sys
import uuid
import requests
import wave
import base64
import hashlib
from imp import reload
import time
reload(sys)
YOUDAO_URL = 'https://openapi.youdao.com/asrapi'
APP_KEY = '您的应用ID'
APP_SECRET = '您的应用密钥'
def truncate(q):
if q is None:
return None
size = len(q)
return q if size <= 20 else q[0:10] + str(size) + q[size-10:size]
def encrypt(signStr):
hash_algorithm = hashlib.sha256()
hash_algorithm.update(signStr.encode('utf-8'))
return hash_algorithm.hexdigest()
def do_request(data):
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
return requests.post(YOUDAO_URL, data=data, headers=headers)
def connect():
audio_file_path = '音频的路径'
lang_type = '合成文本的语言类型'
extension = audio_file_path[audio_file_path.rindex('.')+1:]
if extension != 'wav':
print('不支持的音频类型')
sys.exit(1)
wav_info = wave.open(audio_file_path, 'rb')
sample_rate = wav_info.getframerate()
nchannels = wav_info.getnchannels()
wav_info.close()
with open(audio_file_path, 'rb') as file_wav:
q = base64.b64encode(file_wav.read()).decode('utf-8')
data = {}
curtime = str(int(time.time()))
data['curtime'] = curtime
salt = str(uuid.uuid1())
signStr = APP_KEY + truncate(q) + salt + curtime + APP_SECRET
sign = encrypt(signStr)
data['appKey'] = APP_KEY
data['q'] = q
data['salt'] = salt
data['sign'] = sign
data['signType'] = "v2"
data['langType'] = lang_type
data['rate'] = sample_rate
data['format'] = 'wav'
data['channel'] = nchannels
data['type'] = 1
response = do_request(data)
print(response.content)
if __name__ == '__main__':
connect()
using System;
using System.IO;
using System.Web;
using System.Net;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Security.Cryptography;
namespace zhiyun_csharp_demo
{
class AsrV2Demo
{
public static void Main()
{
String url = "https://openapi.youdao.com/asrapi";
Dictionary<String, String> dic = new Dictionary<String, String>();
string q = LoadAsBase64("音频的路径");
string appKey = "您的应用ID";
string appSecret = "您的应用密钥";
string langType = "合成文本的语言类型";
string format = "wav";
string rate = "16000";
string channel = "1";
string type = "1";
string salt = Guid.NewGuid().ToString();
TimeSpan ts = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc));
long millis = (long) ts.TotalMilliseconds;
string curtime = Convert.ToString(millis / 1000);
dic.Add("curtime", curtime);
string signStr = appKey + Truncate(q) + salt + curtime + appSecret;;
string sign = ComputeHash(signStr, new SHA256CryptoServiceProvider());
dic.Add("q", System.Web.HttpUtility.UrlEncode(q));
dic.Add("appKey", appKey);
dic.Add("langType", langType);
dic.Add("format", format);
dic.Add("rate", rate);
dic.Add("channel", channel);
dic.Add("type", type);
dic.Add("salt", salt);
dic.Add("sign", sign);
dic.Add("signType", "v2");
string result = Post(url, dic);
Console.WriteLine(result);
}
protected static string ComputeHash(string input, HashAlgorithm algorithm)
{
Byte[] inputBytes = Encoding.UTF8.GetBytes(input);
Byte[] hashedBytes = algorithm.ComputeHash(inputBytes);
return BitConverter.ToString(hashedBytes).Replace("-", "");
}
protected static string Truncate(string q)
{
if (q == null)
{
return null;
}
int len = q.Length;
return len <= 20 ? q : (q.Substring(0, 10) + len + q.Substring(len - 10, 10));
}
protected static string LoadAsBase64(string filename)
{
try
{
FileStream filestream = new FileStream(filename, FileMode.Open);
byte[] arr = new byte[filestream.Length];
filestream.Position = 0;
filestream.Read(arr, 0, (int)filestream.Length);
filestream.Close();
return Convert.ToBase64String(arr);
}
catch (Exception ex)
{
return null;
}
}
protected static string Post(string url, Dictionary<String, String> dic)
{
string result = "";
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
StringBuilder builder = new StringBuilder();
int i = 0;
foreach (var item in dic)
{
if (i > 0)
builder.Append("&");
builder.AppendFormat("{0}={1}", item.Key, item.Value);
i++;
}
byte[] data = Encoding.UTF8.GetBytes(builder.ToString());
req.ContentLength = data.Length;
using (Stream reqStream = req.GetRequestStream())
{
reqStream.Write(data, 0, data.Length);
reqStream.Close();
}
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
Stream stream = resp.GetResponseStream();
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
{
result = reader.ReadToEnd();
}
return result;
}
}
}
<?php
define("CURL_TIMEOUT", 2000);
define("URL", "https://openapi.youdao.com/asrapi");
define("APP_KEY", "您的应用ID"); // 替换为您的应用ID
define("SEC_KEY", "您的应用密钥"); // 替换为您的密钥
function do_request($q, $langType, $channel, $rate, $format)
{
$salt = create_guid();
$args = array(
'q' => $q,
'appKey' => APP_KEY,
'salt' => $salt,
'langType' => $langType,
'channel' => $channel,
'rate' => $rate,
'format' => $format,
'type' => "1",
'signType' => "v2",
);
$curtime = strtotime("now");
$args['curtime'] = $curtime;
$signStr = APP_KEY . truncate($q) . $salt . $curtime . SEC_KEY;
$args['sign'] = hash("sha256", $signStr);
$ret = call(URL, $args);
print_r($ret);
$ret = json_decode($ret, true);
return $ret;
}
// 发起网络请求
function call($url, $args=null, $method="post", $testflag = 0, $timeout = CURL_TIMEOUT, $headers=array())
{
$ret = false;
$i = 0;
while($ret === false)
{
if($i > 1)
break;
if($i > 0)
{
sleep(1);
}
$ret = callOnce($url, $args, $method, false, $timeout, $headers);
$i++;
}
return $ret;
}
function callOnce($url, $args=null, $method="post", $withCookie = false, $timeout = CURL_TIMEOUT, $headers=array())
{
$ch = curl_init();
if($method == "post")
{
$data = convert($args);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_POST, 1);
}
else
{
$data = convert($args);
if($data)
{
if(stripos($url, "?") > 0)
{
$url .= "&$data";
}
else
{
$url .= "?$data";
}
}
}
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
if(!empty($headers))
{
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
}
if($withCookie)
{
curl_setopt($ch, CURLOPT_COOKIEJAR, $_COOKIE);
}
$r = curl_exec($ch);
curl_close($ch);
return $r;
}
function convert(&$args)
{
$data = '';
if (is_array($args))
{
foreach ($args as $key=>$val)
{
if (is_array($val))
{
foreach ($val as $k=>$v)
{
$data .= $key.'['.$k.']='.rawurlencode($v).'&';
}
}
else
{
$data .="$key=".rawurlencode($val)."&";
}
}
return trim($data, "&");
}
return $args;
}
// uuid generator
function create_guid(){
$microTime = microtime();
list($a_dec, $a_sec) = explode(" ", $microTime);
$dec_hex = dechex($a_dec* 1000000);
$sec_hex = dechex($a_sec);
ensure_length($dec_hex, 5);
ensure_length($sec_hex, 6);
$guid = "";
$guid .= $dec_hex;
$guid .= create_guid_section(3);
$guid .= '-';
$guid .= create_guid_section(4);
$guid .= '-';
$guid .= create_guid_section(4);
$guid .= '-';
$guid .= create_guid_section(4);
$guid .= '-';
$guid .= $sec_hex;
$guid .= create_guid_section(6);
return $guid;
}
function truncate($q) {
$len = abslength($q);
return $len <= 20 ? $q : (mb_substr($q, 0, 10) . $len . mb_substr($q, $len - 10, $len));
}
function abslength($str)
{
if(empty($str)){
return 0;
}
if(function_exists('mb_strlen')){
return mb_strlen($str,'utf-8');
}
else {
preg_match_all("/./u", $str, $ar);
return count($ar[0]);
}
}
function ensure_length(&$string, $length){
$strlen = strlen($string);
if($strlen < $length)
{
$string = str_pad($string, $length, "0");
}
else if($strlen > $length)
{
$string = substr($string, 0, $length);
}
}
function create_guid_section($characters){
$return = "";
for($i = 0; $i < $characters; $i++)
{
$return .= dechex(mt_rand(0,15));
}
return $return;
}
$file = "音频的路径";
$langType = "合成文本的语言类型";
$fp = fopen($file, "r") or die("Can't open file");
// base64编码
$q = chunk_split(base64_encode(fread($fp, filesize($file))));
fclose($fp);
do_request($q, $langType, "1", 16000, "wav");
?>