干货 | 调用AI api 实现网页文字朗读
京东云上提供了足够多的人工智能api,并且都使用了http的方式进行了封装,用户可以方便在自己的系统中接入京东云的ai能力。今天就是介绍一下如何编写很少的代码就能使用京东云的语音合成api在网页中实现文字朗读,最终实现效果,延迟小,支持主流设备,声调优美,还能男女生切换。
最终效果
最终效果,微信打开链接,点击播放按钮则可以进行文字朗读。
Api介绍
京东云AI API使用Restful接口风格,同时提供了java和python的sdk,使用sdk能够方便的封装参数,调用api获得数据。
为了提升调用方的响应速度,语音合成api采用了分段合成的模式,所以调用的时候在后端逻辑中按顺序多次调用,将音频数据以数据流的形式回写给前端。
获取AK/SK
访问京东云api需要获取 ak sk ,配合sdk使用;
进入京东云控制台-账号管理-Access Key管理,创建并获取Access Key。
后端音频流合成
这里给出后端部分源码,实现一个controller,开发一个get请求方法,参数封装的逻辑全都提炼出单独的方法,代码逻辑结构简单易懂。代码使用fastJson处理参数,另外引用了京东云sdk,其余都是jdk自带的api,依赖很少。
1import com.alibaba.fastjson.JSON;
2import com.alibaba.fastjson.JSONObject;
3import com.wxapi.WxApiCall.WxApiCall;
4import com.wxapi.model.RequestModel;
5
6import org.springframework.stereotype.Controller;
7import org.springframework.web.bind.annotation.GetMapping;
8import org.springframework.web.bind.annotation.RequestHeader;
9
10import javax.servlet.http.HttpServletRequest;
11import javax.servlet.http.HttpServletResponse;
12import java.io.IOException;
13import java.io.OutputStream;
14import java.util.Base64;
15import java.util.HashMap;
16import java.util.Map;
17
18@Controller
19public class TTSControllerExample {
20 //url appkey secretkey
21 private static final String url = "https://aiapi.jdcloud.com/jdai/tts";
22 private static final String appKey = "";
23 private static final String secretKey = "";
24
25 @GetMapping("/tts/stream/example")
26 public void ttsStream(
27 @RequestHeader(value = "Range", required = false) String range,
28 HttpServletRequest req,
29 HttpServletResponse resp) {
30
31 //应对safari的第一次确认请求携带header Range:bytes=0-1,此时回写1byte数据,防止错误
32 if ("bytes=0-1".equals(range)) {
33 try {
34 byte[] temp = new byte['a'];
35 resp.setHeader("Content-Type", "audio/mp3");
36 OutputStream out = resp.getOutputStream();
37 out.write(temp);
38} catch (IOException e) {
39 e.printStackTrace();
40 }
41 return;
42 }
43 //封装输入参数
44 Map queryMap = processQueryParam(req);
45 String text = req.getParameter("text");
46//封装api调用请求报文
47 RequestModel requestModel = getBaseRequestModel(queryMap, text);
48 try {
49//回写音频数据给前端
50 writeTtsStream(resp, requestModel);
51} catch (IOException e) {
52 e.printStackTrace();
53 }
54 }
55
56 /**
57 * 将前端输入参数封装为api调用的请求对象,同时设置url appkey secaretKey
58 * @param queryMap
59 * @param bodyStr
60 * @return
61 */
62 private RequestModel getBaseRequestModel(Map queryMap, String bodyStr) {
63 RequestModel requestModel = new RequestModel();
64 requestModel.setGwUrl(url);
65 requestModel.setAppkey(appKey);
66 requestModel.setSecretKey(secretKey);
67 requestModel.setQueryParams(queryMap);
68 requestModel.setBodyStr(bodyStr);
69 return requestModel;
70 }
71
72 /**
73 * 流式api调用,需要将sequenceId 依次递增,用该方法进行设置请求对象sequenceId
74 * @param sequenceId
75 * @param requestModel
76 * @return
77 */
78 private RequestModel changeSequenceId(int sequenceId, RequestModel requestModel) {
79 requestModel.getQueryParams().put("Sequence-Id", sequenceId);
80 return requestModel;
81 }
82
83 /**
84 * 将request中的请求参数封装为api调用请求对象中的queryMap
85 * @param req
86 * @return
87 */
88 private Map processQueryParam(HttpServletRequest req) {
89 String reqid = req.getParameter("reqid");
90 int tim = Integer.parseInt(req.getParameter("tim"));
91 String sp = req.getParameter("sp");
92
93 JSONObject parameters = new JSONObject(8);
94 parameters.put("tim", tim);
95 parameters.put("sr", 24000);
96 parameters.put("sp", sp);
97 parameters.put("vol", 2.0);
98 parameters.put("tte", 0);
99 parameters.put("aue", 3);
100
101 JSONObject property = new JSONObject(4);
102 property.put("platform", "Linux");
103 property.put("version", "1.0.0");
104 property.put("parameters", parameters);
105
106 Map<String, Object> queryMap = new HashMap<>();
107//访问参数
108 queryMap.put("Service-Type", "synthesis");
109 queryMap.put("Request-Id", reqid);
110 queryMap.put("Protocol", 1);
111 queryMap.put("Net-State", 1);
112 queryMap.put("Applicator", 1);
113 queryMap.put("Property", property.toJSONString());
114
115 return queryMap;
116 }
117
118 /**
119 * 循环调用api,将音频数据回写到response对象
120 * @param resp
121 * @param requestModel
122 * @throws IOException
123 */
124 public void writeTtsStream(HttpServletResponse resp, RequestModel requestModel) throws IOException {
125 //分段获取音频sequenceId从1递增
126 int sequenceId = 1;
127 changeSequenceId(sequenceId, requestModel);
128 //设置返回报文头内容类型为audio/mp3
129 resp.setHeader("Content-Type", "audio/mp3");
130 //api请求sdk对象
131 WxApiCall call = new WxApiCall();
132 //获取输出流用于输出音频流
133 OutputStream out = resp.getOutputStream();
134 call.setModel(requestModel);
135 //解析返回报文,获得status
136 String response = call.request();
137 JSONObject jsonObject = JSON.parseObject(response);
138 JSONObject data = jsonObject.getJSONObject("result");
139 //第一次请求增加校验,如果错误则向前端回写500错误码
140 if (data.getIntValue("status") != 0) {
141 resp.sendError(500, data.getString("message"));
142 return;
143 }
144 //推送实际音频数据
145 String audio = data.getString("audio");
146 byte[] part = Base64.getDecoder().decode(audio);
147 out.write(part);
148 out.flush();
149 //判断是否已结束,多次请求对应多个index,index<0 代表最后一个包
150 if (data.getIntValue("index") < 0) {
151 return;
152 }
153 //循环推送剩余部分音频
154 while (data.getIntValue("index") >= 0) {
155 //sequenceid 递增
156 sequenceId = sequenceId + 1;
157 changeSequenceId(sequenceId, requestModel);
158 //请求api获得新的音频数据
159 call.setModel(requestModel);
160 response = call.request();
161 jsonObject = JSON.parseObject(response);
162 data = jsonObject.getJSONObject("result");
163 audio = data.getString("audio");
164 part = Base64.getDecoder().decode(audio);
165 //回写新的音频数据
166 out.write(part);
167 out.flush();
168 }
169 }
170
171
172
173前端audio播放朗读
174前端部分给出在vue 模块化开发中的script部分,由于采用html5的audio进行语音播放,为了兼容性需要引用howler.js (npm install howler),主要逻辑为根据设置的参数和待朗读的文字拼接一个url,调用howler.js 中的api进行播放。
175
176<script>
177import {Howl, Howler} from 'howler'
178export default {
179 data() {
180 return {
181 news: { // 新闻内容
182 ……
183 },
184 role: 1, // 0女声,1男声
185 speed: 1, // 播放速度
186 curIndex: -1, // 播放的段落在所有段落中的顺序,与用户交互显示相关,与流式播放无关
187 sound: null, // 页面唯一的指向howler实例的变量
188 status: 'empty' // load,pause,stop,empty 仅与用户交互显示相关,与流式播放显示无关
189 }
190 },
191 methods: {
192 generateUUID () { // 生成uuid
193 let d = Date.now()
194 return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
195 let r = (d + Math.random() * 16) % 16 | 0
196 d = Math.floor(d / 16)
197 return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16)
198 })
199 },
200 audioSrc (txt) { // 生成获取音频的链接
201 let content = encodeURI(txt) // 文字编码
202 return `http://neuhubdemo.jd.com/api/tts/streamv2?reqid=${
203 this.generateUUID() // requestID
204 }&text=${
205 content // 编码后的文字内容
206 }&tim=${
207 this.role // 男声 or 女声
208 }&sp=${
209 this.speed // 播放速度
210 }`
211 },
212 /**
213 * 获取文案对应的流式音频
214 *
215 * 使用howler能够解决部分手机浏览器(eg:UC)的兼容问题,
216 * 但解决ios上微信和safari的兼容问题,
217 * 需要后端通过{range:bytes=0-1}这个header字段对请求进行控制
218 * @param {String 待转音频的文案} txt
219 */
220 howlerPlay(txt) {
221 if (this.sound) {
222 this.sound.unload() // 若sound已有值,则销毁原对象
223 }
224 let self = this
225 this.status = 'load'
226 this.sound = new Howl({
227 src: `${this.audioSrc(txt)}`,
228 html5: true, // 必须!A live stream can only be played through HTML5 Audio.
229 format: ['mp3', 'aac'],
230 // 以下onplay、onpause、onend均为控制显示相关
231 onplay() {
232 self.status = 'pause'
233 },
234 onpause: function() {
235 self.status = 'stop'
236 },
237 onend: function() {
238 self.status = 'stop'
239 }
240 });
241 this.sound.play()
242 },
243 // 控制用户交互
244 play (txt, index) {
245 if (this.curIndex === index) {
246 if (this.status === 'stop') {
247 this.sound.play()
248 } else {
249 this.sound.pause()
250 }
251 } else {
252 this.curIndex = index
253 this.howlerPlay(txt)
254 }
255 }
256 }
257}
258</script>
看完这个操作文档是不是跃跃欲试?对AI也想了解更多?
本周六我们为大家准备了【从“智慧零售”到“无人仓储”,揭秘京东人工智能技术的实践与应用】“京东云技术沙龙AI专场 ”!现场将会有技术专家为大家答疑解惑。

欢迎点击“链接”了解更多精彩内容
干货 | 调用AI api 实现网页文字朗读的更多相关文章
- python3下搜狗AI API实现
1.背景 a.搜狗也发布了自己的人工智能 api,包括身份证ocr.名片ocr.文本翻译等API,初试感觉准确率一般般. b.基于python3. c.也有自己的签名生成这块,有了鹅厂的底子,相对写起 ...
- WebApi系列~通过HttpClient来调用Web Api接口
回到目录 HttpClient是一个被封装好的类,主要用于Http的通讯,它在.net,java,oc中都有被实现,当然,我只会.net,所以,只讲.net中的HttpClient去调用Web Api ...
- 以短链服务为例,探讨免AppKey、免认证、Ajax跨域调用新浪微博API
新浪微博的API官方提供了很多种调用方式,支持编程的,归根结底就是两种: 1.基于Oauth协议,使用Open API.(http://open.weibo.com/wiki/%E6%8E%88%E6 ...
- 通过HttpClient来调用Web Api接口
回到目录 HttpClient是一个被封装好的类,主要用于Http的通讯,它在.net,java,oc中都有被实现,当然,我只会.net,所以,只讲.net中的HttpClient去调用Web Api ...
- C#调用Geocoding API进行地理编码与逆编码
使用C#调用Geocoding API来将地址转为经纬度,或者将经纬度转变为具体的地址. Geocoding API的详细介绍参见:http://developer.baidu.com/map/web ...
- PHP:基于百度大脑api实现OCR文字识别
有个项目要用到文字识别,网上找了很多资料,效果不是很好,偶然的机会,接触到百度大脑.百度大脑提供了很多解决方案,其中一个就是文字识别,百度提供了三种文字识别,分别是银行卡识别.身份证识别和通用文字识别 ...
- ajax调用.net API项目跨域问题解决
ajax调用.net API项目,经常提示跨域问题.添加如下节点代码解决:httpProtocol <system.webServer> <handlers> <remo ...
- Android Studio快速集成讯飞SDK实现文字朗读功能
今天,我们来学习一下怎么在Android Studio快速集成讯飞SDK实现文字朗读功能,先看一下效果图: 第一步 :了解TTS语音服务 TTS的全称为Text To Speech,即“从文本到语音” ...
- 对TControl和TWinControl相同与不同之处的深刻理解(每一个WinControl就相当于扮演了整个Windows的窗口管理角色,主要是窗口显示和窗口大小)——TWinControl就两个作用(管理子控件的功能和调用句柄API的功能)
TControl是图形控件,它本身没有句柄,所以不能直接使用WINAPI显示,调整位置,发消息等等,只能想办法间接取得想要的效果,但是可以直接使用一些不需要句柄的API,比如InvalidateRec ...
随机推荐
- 《Python爬虫技术:深入理解原理、技术与开发》已经出版,送Python基础视频课程
好消息,<Python爬虫技术:深入理解原理.技术与开发>已经出版!!! JetBrains官方推荐图书!JetBrains官大中华区市场部经理赵磊作序!送Python基础视频课程!J ...
- 141-PHP类的抽象方法和继承实例(一)
<?php abstract class ren{ //定义人类 //定义成员属性 protected $name=''; protected $age=0; //定义成员方法 public f ...
- 微信小程序添加背景图片的坑
给微信小程序页面加载背景图片解决方案 直接附上原文地址: 给微信小程序页面加载背景图片解决方案 - YUSIR 完美CODING世界 - CSDN博客 https://blog.csdn.net/y ...
- Docker 搭建开源 CMDB平台 “OpsManage” 之 Redis
整体结构如下图 先来在 172.16.0.200 安装docker-ce (新)或 docker-io(旧) 0: Docker-ce (新版本 Docker version 17. ...
- Day 19:Properties配置文件类、打印流(printStream) 、 编码与解码
Properties(配置文件类): 主要用于生产配置文件与读取配置文件的信息. Properties要注意的细节: 1. 如果配置文件的信息一旦使用了中文,那么在使用store方法生成配置文件的时 ...
- Egret Engine 2D - 矢量绘图
绘制矩形 drawRect 绘制矩形边 lineStyle( 10, 0x00ff00 清空绘图 clear 绘制园形 drawCircle 绘制直线 moveTo lineTo 绘制曲线 cur ...
- 数据结构顺序表中Sqlist *L,&L,Sqlist *&L
//定义顺序表L的结构体 typedef struct { Elemtype data[MaxSize]: int length; }SqList; //建立顺序表 void CreateList(S ...
- k8s deployment yam 文件分析
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: namespace: labels:spec: replicas: #设 ...
- 服务器搭建---Linux安装Node.js
先去官网下载:https://nodejs.org/en/download/ 把压缩包上传到服务器的/usr/local/soft(博主习惯)文件夹下 解压文件: cd /usr/local/sof ...
- Linux 目录变化监听 - python代码实现
在python中 文件监控主要有两个库, 一个是pyinotify ( https://github.com/seb-m/pyinotify/wiki ),pyinotify依赖于Linux平台的in ...