調(diào)用AIapi實(shí)現(xiàn)網(wǎng)頁文字朗讀的方法

本篇內(nèi)容主要講解“調(diào)用AI api實(shí)現(xiàn)網(wǎng)頁文字朗讀的方法”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“調(diào)用AI api實(shí)現(xiàn)網(wǎng)頁文字朗讀的方法”吧!

創(chuàng)新互聯(lián)建站主營襄垣網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營網(wǎng)站建設(shè)方案,App定制開發(fā),襄垣h5微信小程序開發(fā)搭建,襄垣網(wǎng)站營銷推廣歡迎襄垣等地區(qū)企業(yè)咨詢

京東云上提供了足夠多的人工智能api,并且都使用了http的方式進(jìn)行了封裝,用戶可以方便在自己的系統(tǒng)中接入京東云的ai能力。今天就是介紹一下如何編寫很少的代碼就能使用京東云的語音合成api在網(wǎng)頁中實(shí)現(xiàn)文字朗讀,最終實(shí)現(xiàn)效果,延遲小,支持主流設(shè)備,聲調(diào)優(yōu)美,還能男女生切換。

最終效果

調(diào)用AI api實(shí)現(xiàn)網(wǎng)頁文字朗讀的方法

最終效果,微信打開鏈接,點(diǎn)擊播放按鈕則可以進(jìn)行文字朗讀。

Api介紹

調(diào)用AI api實(shí)現(xiàn)網(wǎng)頁文字朗讀的方法

京東云AI API使用Restful接口風(fēng)格,同時(shí)提供了java和python的sdk,使用sdk能夠方便的封裝參數(shù),調(diào)用api獲得數(shù)據(jù)。

為了提升調(diào)用方的響應(yīng)速度,語音合成api采用了分段合成的模式,所以調(diào)用的時(shí)候在后端邏輯中按順序多次調(diào)用,將音頻數(shù)據(jù)以數(shù)據(jù)流的形式回寫給前端。

獲取AK/SK

訪問京東云api需要獲取 ak sk ,配合sdk使用;
進(jìn)入京東云控制臺(tái)-賬號(hào)管理-Access Key管理,創(chuàng)建并獲取Access Key。

調(diào)用AI api實(shí)現(xiàn)網(wǎng)頁文字朗讀的方法

后端音頻流合成

調(diào)用AI api實(shí)現(xiàn)網(wǎng)頁文字朗讀的方法

這里給出后端部分源碼,實(shí)現(xiàn)一個(gè)controller,開發(fā)一個(gè)get請(qǐng)求方法,參數(shù)封裝的邏輯全都提煉出單獨(dú)的方法,代碼邏輯結(jié)構(gòu)簡(jiǎn)單易懂。代碼使用fastJson處理參數(shù),另外引用了京東云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        //應(yīng)對(duì)safari的第一次確認(rèn)請(qǐng)求攜帶header Range:bytes=0-1,此時(shí)回寫1byte數(shù)據(jù),防止錯(cuò)誤
 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        //封裝輸入?yún)?shù)
 44        Map queryMap = processQueryParam(req);
 45        String text = req.getParameter("text");
 46 //封裝api調(diào)用請(qǐng)求報(bào)文
 47        RequestModel requestModel = getBaseRequestModel(queryMap, text);
 48        try {
 49 //回寫音頻數(shù)據(jù)給前端
 50            writeTtsStream(resp, requestModel);
 51 } catch (IOException e) {
 52            e.printStackTrace();
 53        }
 54    }
 55
 56    /**
 57     * 將前端輸入?yún)?shù)封裝為api調(diào)用的請(qǐng)求對(duì)象,同時(shí)設(shè)置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調(diào)用,需要將sequenceId 依次遞增,用該方法進(jìn)行設(shè)置請(qǐng)求對(duì)象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中的請(qǐng)求參數(shù)封裝為api調(diào)用請(qǐng)求對(duì)象中的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 //訪問參數(shù)
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     * 循環(huán)調(diào)用api,將音頻數(shù)據(jù)回寫到response對(duì)象
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        //設(shè)置返回報(bào)文頭內(nèi)容類型為audio/mp3
129        resp.setHeader("Content-Type", "audio/mp3");
130        //api請(qǐng)求sdk對(duì)象
131        WxApiCall call = new WxApiCall();
132        //獲取輸出流用于輸出音頻流
133        OutputStream out = resp.getOutputStream();
134        call.setModel(requestModel);
135        //解析返回報(bào)文,獲得status
136        String response = call.request();
137        JSONObject jsonObject = JSON.parseObject(response);
138        JSONObject data = jsonObject.getJSONObject("result");
139        //第一次請(qǐng)求增加校驗(yàn),如果錯(cuò)誤則向前端回寫500錯(cuò)誤碼
140        if (data.getIntValue("status") != 0) {
141            resp.sendError(500, data.getString("message"));
142            return;
143        }
144        //推送實(shí)際音頻數(shù)據(jù)
145        String audio = data.getString("audio");
146        byte[] part = Base64.getDecoder().decode(audio);
147        out.write(part);
148        out.flush();
149        //判斷是否已結(jié)束,多次請(qǐng)求對(duì)應(yīng)多個(gè)index,index<0 代表最后一個(gè)包
150        if (data.getIntValue("index") < 0) {
151            return;
152        }
153        //循環(huán)推送剩余部分音頻
154        while (data.getIntValue("index") >= 0) {
155            //sequenceid 遞增
156            sequenceId = sequenceId + 1;
157            changeSequenceId(sequenceId, requestModel);
158            //請(qǐng)求api獲得新的音頻數(shù)據(jù)
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            //回寫新的音頻數(shù)據(jù)
166            out.write(part);
167            out.flush();
168        }
169    }
170
171
172
173 前端audio播放朗讀
174 前端部分給出在vue 模塊化開發(fā)中的script部分,由于采用html5的audio進(jìn)行語音播放,為了兼容性需要引用howler.js (npm install howler),主要邏輯為根據(jù)設(shè)置的參數(shù)和待朗讀的文字拼接一個(gè)url,調(diào)用howler.js 中的api進(jìn)行播放。
175
176 <script>
177 import {Howl, Howler} from 'howler'
178 export default {
179  data() {
180    return {
181      news: { // 新聞內(nèi)容
182        ……
183      },
184      role: 1, // 0女聲,1男聲
185      speed: 1, // 播放速度
186      curIndex: -1, // 播放的段落在所有段落中的順序,與用戶交互顯示相關(guān),與流式播放無關(guān)
187      sound: null, // 頁面唯一的指向howler實(shí)例的變量
188      status: 'empty' // load,pause,stop,empty 僅與用戶交互顯示相關(guān),與流式播放顯示無關(guān)
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 // 編碼后的文字內(nèi)容
206        }&tim=${
207          this.role // 男聲 or 女聲
208        }&sp=${
209          this.speed // 播放速度
210        }`
211    },
212    /** 
213     * 獲取文案對(duì)應(yīng)的流式音頻
214     * 
215     * 使用howler能夠解決部分手機(jī)瀏覽器(eg:UC)的兼容問題,
216     * 但解決ios上微信和safari的兼容問題,
217     * 需要后端通過{range:bytes=0-1}這個(gè)header字段對(duì)請(qǐng)求進(jìn)行控制
218     *  @param {String 待轉(zhuǎn)音頻的文案} txt
219    */
220    howlerPlay(txt) { 
221      if (this.sound) {
222        this.sound.unload() // 若sound已有值,則銷毀原對(duì)象
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均為控制顯示相關(guān)
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>

到此,相信大家對(duì)“調(diào)用AI api實(shí)現(xiàn)網(wǎng)頁文字朗讀的方法”有了更深的了解,不妨來實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

網(wǎng)站欄目:調(diào)用AIapi實(shí)現(xiàn)網(wǎng)頁文字朗讀的方法
標(biāo)題路徑:http://bm7419.com/article14/jcihde.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供用戶體驗(yàn)網(wǎng)站維護(hù)、小程序開發(fā)、品牌網(wǎng)站設(shè)計(jì)電子商務(wù)、網(wǎng)站設(shè)計(jì)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站托管運(yùn)營