java调用sdk_干货 | 调用AI api 实现网页文字朗读

ba47bb85fc6e17d8fd99d31ed76ec033.png
京东云上提供了足够多的人工智能api,并且都使用了http的方式进行了封装,用户可以方便在自己的系统中接入京东云的ai能力。今天就是介绍一下如何编写很少的代码就能使用京东云的语音合成api在网页中实现文字朗读,最终实现效果,延迟小,支持主流设备,声调优美,还能男女生切换。

最终效果

d1c73bba524db41034018df3798ab1ff.png


最终效果,微信打开链接,点击播放按钮则可以进行文字朗读。

Api介绍

81005690f4ac1d090f85c44f9424fa31.png


京东云AI API使用Restful接口风格,同时提供了java和python的sdk,使用sdk能够方便的封装参数,调用api获得数据。
为了提升调用方的响应速度,语音合成api采用了分段合成的模式,所以调用的时候在后端逻辑中按顺序多次调用,将音频数据以数据流的形式回写给前端。

获取AK/SK

访问京东云api需要获取 ak sk ,配合sdk使用;
进入京东云控制台-账号管理-Access Key管理,创建并获取Access Key。

12cac4508aedabd3e07b909dafb59a4b.png

后端音频流合成

a50d91fc5f8b814109cb2489ea309dec.png


这里给出后端部分源码,实现一个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;
  5
  6 import org.springframework.stereotype.Controller;
  7 import org.springframework.web.bind.annotation.GetMapping;
  8 import org.springframework.web.bind.annotation.RequestHeader;
  9
 10 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;
 17
 18 @Controller
 19 public 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>
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专场 ”!现场将会有技术专家为大家答疑解惑。

8c39fc924512c6b78bb58327b051211c.png

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

1f5b4cccb0434d444d7059d4c0efef95.png

bfcf35cfc2c8cd043ab0ff370407406b.gif

d29db2af9245e474e0b7f08a75c325ef.gif
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值