zoukankan      html  css  js  c++  java
  • 使用腾讯语音合成技术生成有声书

      背景:不知是否在博客园看到的腾讯云平台广告,被AI接口几个项目吸引住了,其中有个   语音合成  接口在这里安利一下,还挺好玩。这个接口提供将一段文字转换成语音的功能,支持中文、英文,遗憾的是暂时无法通过自己的声音进行训练,推出自己独有声音的音频文件:) 不过总体来说,还是相当不错啦,附件中是我用这个接口转换的样例音频文件。

    DEMO实测,代码案例简单概述:

    首先,调用接口肯定得申请appkey,secrect等一堆东西,在这里申请

    申请,完成后会获得公共请求参数必须的信息,然后接口调用分为直接http请求与使用官方版本的sdk调用2种方式,建议使用sdk调用的方式,避免还得自己加sign。sdk调用的方式很简单,测试demo如下:

     @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;
        }
    

     至此,基本可以满足咱们转换小说的需要啦!!!今天也上传了第一套专辑《孙子兵法》 到喜马拉雅试试水,大家有感兴趣的可以去听一下语音合成的效果,如果给您带来帮助,请不要吝惜动下手指 帮忙点赞哟!

    代码、文字文本交流可以私信也可以评论中留言,

    想听书的再也不用担心没书可听了,有想听书的朋友可以私信我有版权的文本内容,帮你转换哦。走路、吃饭、开车,想听就听……

    百度语音合成SDK

    接着上次的内容,又找了下百度和阿里的语音合成sdk,发现百度和阿里的相对腾讯的语音合成貌似更加成熟,使用方法什么的就不赘述了,直接上API地址,懂的自然懂。

    不论是百度的还是腾讯的语音合成接口单次请求的字符数是有限制的,而我们的小说文档都很长,在之前的处理当中我是定长100处理的。会造成明明连着的一个成语或者词组,比如 “你好啊,我亲爱的朋友”,如果按照定长2个字符来处理,就会变成“你好”、“啊,我”等等,在生成音频文件合成后,连续的读起来时会发现这种断句有明显的停顿,体验很不好,因此增加如下方式,默认按照定长字符处理,但是会智能化的去寻找离定长最近的逗号、或者句号来进行断句。这样就不会出现生硬的分开本应连在一起读的词语了

    处理代码如下:

      /**
         * 按照给定的字符串长度在指定文本中查找最接近指定长度的逗号或者句号的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;
        }
    
  • 相关阅读:
    js动态添加CSS
    LINUX命令行回滚SVN版本
    LINUX文件名批量修改
    LINUX下文件编码转换 iconv
    ubuntu下eclipse的svn插件使用javahl
    HTML5学习笔记 本地数据库
    正则表达式
    我的第一篇博客
    【分布式锁的演化】分布式锁居然还能用MySQL?
    【分布式锁的演化】终章!手撸ZK分布式锁!
  • 原文地址:https://www.cnblogs.com/falcon-fei/p/9461423.html
Copyright © 2011-2022 走看看