使用騰訊語音合成技術生成有聲書

  背景:不知是否在博客園看到的騰訊雲平臺廣告,被AI接口幾個項目吸引住了,其中有個   語音合成  接口在這裏安利一下,還挺好玩。這個接口提供將一段文字轉換成語音的功能,支持中文、英文,遺憾的是暫時沒法經過本身的聲音進行訓練,推出本身獨有聲音的音頻文件:) 不過整體來講,仍是至關不錯啦,附件中是我用這個接口轉換的樣例音頻文件。java

DEMO實測,代碼案例簡單概述:api

首先,調用接口確定得申請appkey,secrect等一堆東西,在這裏申請 session

申請,完成後會得到公共請求參數必須的信息,而後接口調用分爲直接http請求與使用官方版本的sdk調用2種方式,建議使用sdk調用的方式,避免還得本身加sign。sdk調用的方式很簡單,測試demo以下:app

 @Test
    public void testAi() throws TencentCloudSDKException, IOException, UnsupportedAudioFileException, LineUnavailableException {
        Credential cred = new Credential("你的ID", "你的key");

        AaiClient aaiClient = new AaiClient(cred, "ap-beijing");
        TextToVoiceRequest request = new TextToVoiceRequest();
        request.setProjectId(10144947);
        request.setModelType(1);
        request.setPrimaryLanguage(1);
//        request.setSampleRate();
        request.setSessionId("testsessionid");
        request.setSpeed(1F);
        request.setText("你好啊,你愛我麼");
        request.setVoiceType(1);
        request.setVolume(1F);
        TextToVoiceResponse textToVoiceResponse = aaiClient.TextToVoice(request);
        String audio = textToVoiceResponse.getAudio();

        if (!StringUtils.isEmpty(audio)) {
            System.out.println(audio);


            BASE64Decoder decoder = new BASE64Decoder();
            try {
                byte[] data = decoder.decodeBuffer(audio);
                OutputStream out = new FileOutputStream("d://test1.wav");
                out.write(data);
                out.flush();
                out.close();
            } catch (Exception ex) {

            }
        }
    }

本人喜歡在喜馬拉雅上聽書,也聽小說。看到有不少連普通話都不甚標準的做者有了大量的粉絲,還有打賞。在此我有了一個大膽的想法,在不涉及版權問題的前提下,我是否能夠上傳一大堆小說的音頻內容,以量取勝,。實際測試中發現騰訊語音合成接口默認只支持300個字符,且生成的音頻文件爲BASE64的String字符串,須要進行拼接轉換。固然拼接並非說把api返回的string直接經過一個stringbuilder拼起來就行,由於wav文件結構中是有頭尾標示的,拼接過程中須要去頭尾,拼接轉換部分源碼以下:測試

 @Scheduled(fixedDelay = 1000 * 60 * 60)
    public void toVoice() {
        String textFilePath="D://work/mywork/txt/孫子兵法/計篇.txt";
        String outputPath="D://work/mywork/voice/孫子兵法/計篇.wav";
        try {
            File output=new File(outputPath);
            logger.info("開始獲取文件內文本數據");
            List<String> stringArray = fileManService.getStringArray(textFilePath, 100);
            if (stringArray != null) {
                List<String> voiceWaves=new ArrayList<String>();
                for(String tmpText :stringArray)
                {
                    voiceWaves.add(voiceManService.getWavString(tmpText));
                }
                WavBaseStringMergeUtil wavBaseStringMergeUtil=new WavBaseStringMergeUtil();
                File file=new File(outputPath);
                wavBaseStringMergeUtil.mergeWav(voiceWaves,file);
                logger.info("完成");
            } else {
                logger.info("獲取到的文本內容爲空");
            }

        } catch (Exception e) {
            logger.error("轉換出現異常", e);
        }
    }
private static Header resolveHeader(byte[] Basebytes) throws IOException {
        InputStream fis = new ByteArrayInputStream(Basebytes);
        byte[] byte4 = new byte[4];
        byte[] buffer = new byte[2048];
        int readCount = 0;
        Header header = new Header();
        fis.read(byte4);//RIFF
        fis.read(byte4);
        readCount += 8;
        header.fileSizeOffset = 4;
        header.fileSize = byteArrayToInt(byte4);
        fis.read(byte4);//WAVE
        fis.read(byte4);//fmt
        fis.read(byte4);
        readCount += 12;
        int fmtLen = byteArrayToInt(byte4);
        fis.read(buffer, 0, fmtLen);
        readCount += fmtLen;
        fis.read(byte4);//data or fact
        readCount += 4;
        if (isFmt(byte4, 0)) {//包含fmt段
            fis.read(byte4);
            int factLen = byteArrayToInt(byte4);
            fis.read(buffer, 0, factLen);
            fis.read(byte4);//data
            readCount += 8 + factLen;
        }
        fis.read(byte4);// data size
        int dataLen = byteArrayToInt(byte4);
        header.dataSize = dataLen;
        header.dataSizeOffset = readCount;
        readCount += 4;
        header.dataOffset = readCount;
        header.dataInputStream = fis;
        return header;
    }

 

 

 至此,基本能夠知足我們轉換小說的須要啦!!!今天也上傳了第一套專輯《孫子兵法》 到喜馬拉雅試試水,你們有感興趣的能夠去聽一下語音合成的效果,若是給您帶來幫助,請不要吝惜動下手指 幫忙點贊喲!ui

代碼、文字文本交流能夠私信也能夠評論中留言,this

想聽書的不再用擔憂沒書可聽了,有想聽書的朋友能夠私信我有版權的文本內容,幫你轉換哦。走路、吃飯、開車,想聽就聽……url

 

百度語音合成SDK

接着上次的內容,又找了下百度和阿里的語音合成sdk,發現百度和阿里的相對騰訊的語音合成貌似更加成熟,使用方法什麼的就不贅述了,直接上API地址,懂的天然懂。code

不管是百度的仍是騰訊的語音合成接口單次請求的字符數是有限制的,而咱們的小說文檔都很長,在以前的處理當中我是定長100處理的。會形成明明連着的一個成語或者詞組,好比 「你好啊,我親愛的朋友」,若是按照定長2個字符來處理,就會變成「你好」、「啊,我」等等,在生成音頻文件合成後,連續的讀起來時會發現這種斷句有明顯的停頓,體驗很很差,所以增長以下方式,默認按照定長字符處理,可是會智能化的去尋找離定長最近的逗號、或者句號來進行斷句。這樣就不會出現生硬的分開本應連在一塊兒讀的詞語了blog

處理代碼以下:

  /**
     * 按照給定的字符串長度在指定文本中查找最接近指定長度的逗號或者句號的endindex。若找不到則以指定長度做爲endindex
     * @param inputString
     * @param length
     * @return
     */
    private int getEndIndex(String inputString,int length)
    {
        if(length>inputString.length())
        {
            return inputString.length();
        }
        int retIndex= length;
        for(int i=retIndex-1;i>0;i--)
        {
            if(inputString.charAt(i)=='.' || inputString.charAt(i)=='。' || inputString.charAt(i)==',' || inputString.charAt(i)==',')
            {
                retIndex =i;
                break;
            }
        }
        return retIndex;
    }

    /**
     * 智能拆分文本
     * @param inputString
     * @param length
     * @return
     */
    private List<String> getStrListIntelligence(String inputString,int length)
    {
        List<String> StrList=new ArrayList<String>();
        int indexStart=0;
        while (indexStart<inputString.length())
        {
            //查找endIndex
            int endIndex = this.getEndIndex(inputString.substring(indexStart,indexStart+length<inputString.length()?indexStart+length:inputString.length()),length);
            String tmpString = inputString.substring(indexStart,indexStart+ endIndex);
            StrList.add(tmpString);
            indexStart+=endIndex;
        }
        return  StrList;
    }
相關文章
相關標籤/搜索