京东云上提供了足够多的人工智能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,依赖很少。

  1 import com.alibaba.fastjson.JSON;2 import com.alibaba.fastjson.JSONObject;3 import com.wxapi.WxApiCall.WxApiCall;4 import com.wxapi.model.RequestModel;56 import org.springframework.stereotype.Controller;7 import org.springframework.web.bind.annotation.GetMapping;8 import org.springframework.web.bind.annotation.RequestHeader;910 import javax.servlet.http.HttpServletRequest;11 import javax.servlet.http.HttpServletResponse;12 import java.io.IOException;13 import java.io.OutputStream;14 import java.util.Base64;15 import java.util.HashMap;16 import java.util.Map;1718 @Controller19 public class TTSControllerExample {20    //url appkey secretkey21    private static final String url = "https://aiapi.jdcloud.com/jdai/tts";22    private static final String appKey = "";23    private static final String secretKey = "";2425    @GetMapping("/tts/stream/example")26    public void ttsStream(27            @RequestHeader(value = "Range", required = false) String range,28            HttpServletRequest req,29            HttpServletResponse resp) {3031        //应对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    }5556    /**57     * 将前端输入参数封装为api调用的请求对象,同时设置url appkey secaretKey58     * @param queryMap59     * @param bodyStr60     * @return61     */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    }7172    /**73     * 流式api调用,需要将sequenceId 依次递增,用该方法进行设置请求对象sequenceId74     * @param sequenceId75     * @param requestModel76     * @return77     */78    private RequestModel changeSequenceId(int sequenceId, RequestModel requestModel) {79        requestModel.getQueryParams().put("Sequence-Id", sequenceId);80        return requestModel;81    }8283    /**84     * 将request中的请求参数封装为api调用请求对象中的queryMap85     * @param req86     * @return87     */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");9293        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>
177 import {Howl, Howler} from 'howler'
178 export 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专场 ”!现场将会有技术专家为大家答疑解惑。

点击“阅读原文”即可免费报名参加哦!

转载于:https://my.oschina.net/u/4090830/blog/3076425

干货 | 调用AI api 实现网页文字朗读相关推荐

  1. Linux python PyQt5调用百度API实现图片文字转换

    系统:Linux Mint 18.3 xfce 64bit 参考链接:http://blog.csdn.net/u012236875/article/details/74726035 根据参考链接的代 ...

  2. Android Studio调用百度API(图片文字识别)

    1.登录百度AI平台(https://ai.baidu.com/),下载license文件 包名填写内容: 下载好的文件夹内包含四个文件 (OCR-Android-SDK.md文件里有使用说明) (1 ...

  3. linux查看进程调用接口,查看某个程序都调用哪些api函数

    查看某个程序都调用哪些api函数以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 查看某个程序都调用哪些api函数 vs6 ...

  4. Python——调用百度AI实现图片上文字识别

    Python--调用百度AI实现图片上文字识别 简介 步骤 安装百度AI库 注册百度AI开放平台 调用glob库 调用AipOcr库识别文字 可能会遇到的问题 批量操作 简介 Python免费调用百度 ...

  5. 调用百度ai接口实现图片文字识别详解

    调用百度ai接口实现图片文字识别详解 首先先介绍一下这篇博文是干嘛的,为了不浪费大家时间.公司最近和短视频公司合作,需要监控app的截图上的文字是否符合规范,也就是确保其没有违规的文字.到网上找了一些 ...

  6. ai图像识别python的项目_Python3调用百度AI识别图片中的文字功能示例【测试可用】...

    本文实例讲述了Python3调用百度AI识别图片中的文字功能.分享给大家供大家参考,具体如下: 首先pip install命令安装baidu-aip模块,如下图所示(这里使用pip3 install ...

  7. Java调用百度OCR文字识别API实现图片文字识别软件

    java_baidu_ocr Java调用百度OCR文字识别API实现图片文字识别软件 这是一款小巧方便,强大的文字识别软件,由Java编写,配上了窗口界面 调用了百度ocr文字识别API 识别精度高 ...

  8. Python发送微信消息(文字、图片、文件)给指定好友和微信群(调用Win32 API模拟人的手动操作来发送消息)

    本示例是调用Windows API模拟发送,用Python调用win32api这个库来调用Windows API模拟人的手动操作来发送消息. 在使用前,请将你微信的窗口设置为在最前面,这样就便于程序找 ...

  9. html5d调用百度语音,调用百度API,文字转语音

    前言 文字转语音,运用在什么场景了?一般后台管理系统会用到,提示管理员有"新的订单"."新的任务"等等,管理员可以马上收到,并及时处理.就好比,在餐厅,经常会听 ...

最新文章

  1. 解决Chrome中UEditor插入图片的选择框加载过慢问题
  2. 2021腾讯数字生态大会:腾讯安全聚焦安全共建,护航数字经济发展
  3. dede 二次开发系统迁移到 IIS 时会出现的问题及解决方案
  4. python3 报错 [Errno 5] Input/output error 没有stdout时使用了print
  5. 操作系统原理第四章:线程
  6. STM32系统时钟默认设置
  7. PWN-PRACTICE-CTFSHOW-3
  8. MyBatis映射文件6
  9. STM32 USB数据接收与数据发送程序流程分析
  10. 白话并发冲突与线程同步(3)——Mutex、EventWaitHandle、AutoResetEvent 和 ManualResetEvent...
  11. idea 页面改了 网页没_如何做出高大上的PPT?试试美得令人窒息的网页风格!
  12. [NLP论文阅读]A SIMPLE BUT TOUGH-TO-BEAT BASELINE FOR SENTENCE EMBEDDINGS
  13. UT(XCAP) 参数说明
  14. 软件开发流程都是什么样的呢?
  15. apk 反编译工具及使用
  16. Color类 设置字体颜色、背景颜色
  17. java base是什么文件_JavaBase 面向对象
  18. JavaEE知识点总结详细版(一)计算机是如何进行工作的
  19. 社团管理——原型设计
  20. 小程序 朋友圈,点赞 ,评论,发布动态,功能,局部刷新数据之 -----评论

热门文章

  1. MQTT协议详解 三、MQTT控制包(CONNECT)
  2. 最简单的单层神经网络实现鸢尾花分类
  3. dell的笔记本电脑如果开机总是黑屏 需要开几次才能点亮屏幕
  4. django 注册登录邮箱验证功能
  5. 千里走单骑:06-北京到上海骑记--Day5.风雨回家路
  6. python pandas 怎么判断一天是否为工作日+计算距离特定时间之间的天数
  7. 大数据集群的部署安装
  8. mysql error 1837_MySQL复制错误1837的相关缺陷一例——insert delay在GTID下异常binlog格式...
  9. Python Selenium3.141+Win7(64位)+IEDriverServer(32位版本)+调用IE11卡死的解决
  10. 人工神经网络与深度神经网络