利用百度API实现语音合成

【!最终呈现方式为jar包直接运行,实现原理等可供参考】

运行图
利用百度API实现语音合成_第1张图片
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓以下是正文↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

1.注册百度开发者

(1)语音合成的方式有很多,为了简洁,我们使用第三方语音合成API,这里选择百度语音。详细注册流程官方文档很详细,这里不做过多介绍。传送门,我们需要的是AppID,API Key和Secret Key 如下图
利用百度API实现语音合成_第2张图片
(2)我们要做java后台处理,所以使用java JDK即可,传送门
利用百度API实现语音合成_第3张图片

2使用Idea进行程序的编写

(1)创建普通maven工程即可,相信优秀的你一定都会了,这里不啰嗦了。
(2)引入依赖

		  
		
            org.projectlombok
            lombok
            true
        
          
        
            com.baidu.aip
            java-sdk
            4.11.3
        

(3)创建百度语音合成工具类
ConstansHelper.java

import com.baidu.aip.speech.AipSpeech;
import com.baidu.aip.speech.TtsResponse;
import com.baidu.aip.util.Util;
import lombok.Data;
import org.json.JSONObject;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
/**
 * @Desc:
 * @Author: wangyafei
 * @Date: 2019/9/5 18:04
 * @Version 1.0
 */
@Data
public class ConstansHelper {
    //设置APPID/AK/SK 注册百度开发者会生成
    public static final String APP_ID = "APP_ID ";
    public static final String API_KEY = "API_KEY ";
    public static final String SECRET_KEY = "SECRET_KEY ";
     /**
     * @param spd 语速,取值0-15,默认为5中语速
     * @param pit 音调,取值0-15,默认为5中语调
     * @param vol 音量,取值0-15,默认为5中音量
     * @param per 发音人选择, 0为女声,1为男声,
     *            3为情感合成-度逍遥,
     *            4为情感合成-度丫丫,默认为普通女
     * @param txt 要转换的文本
     */
    public static String makeSpeech(String spd, String pit, String vol, String per, String txt) {
        // 初始化一个AipSpeech
        AipSpeech client = new AipSpeech(APP_ID, API_KEY, SECRET_KEY);

        // 可选:设置网络连接参数
        client.setConnectionTimeoutInMillis(2000);
        client.setSocketTimeoutInMillis(60000);

        // 设置可选参数
        HashMap options = new HashMap();
        if (!"请选择".equals(spd)) {
            options.put("spd", spd);
        }
        if (!"请选择".equals(pit)) {
            options.put("pit", pit);
        }
        if (!"请选择".equals(vol)) {
            options.put("vol", vol);
        }
        if (!"请选择".equals(per)) {
            switch (per){
                case "女声":
                    options.put("per", 0);
                    break;
                case "男声":
                    options.put("per", 1);
                    break;
                case "情感合成-度逍遥":
                    options.put("per", 3);
                    break;
                case "情感合成-度丫丫":
                    options.put("per", 4);
                    break;
            }
        }

        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        String dirName = sdf.format(new Date());
        isChartPathExist("D:\\音频合成\\" + dirName);

        // 调用接口
        //合成文本长度必须小于1024字节,如果本文长度较长,可以采用多次请求的方式。文本长度不可超过限制
        List txtList = splitFunction(txt, 1024);
        byte[] all =null;
        for (String s : txtList) {
            TtsResponse res = client.synthesis(s, "zh", 1, options);
            byte[] data = res.getData();
            if(data != null){
                all = byteMerger(all,data);
            }
        }
        //存储位置,自己决定放到哪里,我放到了D盘
        String fileName  ="D:\\音频合成\\" + dirName + "\\" + (txt.length()>15?txt.substring(0, 15):txt) + "-"+System.currentTimeMillis() + "output.mp3";
        if (all != null) {
            try {
                Util.writeBytesToFileSystem(all, fileName);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        File file = new File(fileName);
        return fileName;

    }


    /**
     * 文件夹磁盘路径 文件夹不存在创建文件夹
     * @param dirPath
     */
    private static void isChartPathExist(String dirPath) {

        File file = new File(dirPath);
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    /**
     * 根据字节长度进行切割
     * 中文在不同编码中占用的字节数是不同的,GBK编码中,一个汉字占两个字节,UTF-8编码格式中,一个汉字占3个字节
     * @param src
     * @param bytes
     * @return
     */
    public static List splitFunction(String src, int bytes){
        try {
            if(src == null){
                return null;
            }
            List splitList = new ArrayList();
            int startIndex = 0;    //字符串截取起始位置
            int endIndex = bytes > src.length() ? src.length() : bytes;  //字符串截取结束位置
            while(startIndex < src.length()){
                String subString = src.substring(startIndex,endIndex);
                //截取的字符串的字节长度大于需要截取的长度时,说明包含中文字符
                //在GBK编码中,一个中文字符占2个字节,UTF-8编码格式,一个中文字符占3个字节。
                while (subString.getBytes("GBK").length > bytes) {
                    --endIndex;
                    subString = src.substring(startIndex,endIndex);
                }
                splitList.add(src.substring(startIndex,endIndex));
                startIndex = endIndex;
                //判断结束位置时要与字符串长度比较(src.length()),之前与字符串的bytes长度比较了,导致越界异常。
                endIndex = (startIndex + bytes) > src.length() ?
                        src.length()  : startIndex+bytes ;

            }
            return splitList;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

    /**
     * 合并两个byte数组
      * @param byte_1
     * @param byte_2
     * @return
     */
    public static byte[] byteMerger(byte[] byte_1, byte[] byte_2){
        if(byte_1 == null){
            return byte_2;
        }
        byte[] byte_3 = new byte[byte_1.length+byte_2.length];
        System.arraycopy(byte_1, 0, byte_3, 0, byte_1.length);
        System.arraycopy(byte_2, 0, byte_3, byte_1.length, byte_2.length);
        return byte_3;
    }
}

(4)创建GUI文件
网上大多数是,直接创建一个Test.form页面的教程, 感觉也不是很方便,这里采用直接写代码的方式。
Container.java


import org.springframework.util.StringUtils;
import org.wyf.yuyindemo.utils.ConstansHelper;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.IOException;


/**
 * @Desc:
 * @Author: wangyafei
 * @Date: 2019/9/6 10:47
 * @Version 1.0
 */
public class Container extends Thread implements ActionListener, WindowListener {

    private static int totalTime = 40;
    private static float totalDiff = 0.0f;
    private TextField txt;
    private TextArea taLog;         //日志输出区域 不可以设置颜色相关
    private JButton jbtStart,jbtReset;       //开始按钮;重置按钮
    private JComboBox spd,pit,vol,per;//语速,语调。音量,角色
    private JLabel tips;

    @Override
    public void run() {
        //画页面
        //父窗体
        JFrame jFrame = new JFrame("语音生成小工具");
        // 创建按钮
        jbtStart = new JButton("生成");
        jbtReset = new JButton("重置");

        jbtStart.addActionListener(this);
        jbtReset.addActionListener(this);

        // 创建相关的文本域
        txt = new TextField("我是文本");

        spd = new JComboBox();
        pit = new JComboBox();
        vol = new JComboBox();
        per = new JComboBox();
        spd.addItem("请选择");
        pit.addItem("请选择");
        vol.addItem("请选择");
        for (int i = 0; i <15 ; i++) {
            spd.addItem(i);
            pit.addItem(i);
            vol.addItem(i);
        }
        per.addItem("请选择");
        per.addItem("女声");
        per.addItem("男声");
        per.addItem("情感合成-度逍遥");
        per.addItem("情感合成-度丫丫");

        JLabel jb1 = new JLabel("   语速");
        JLabel jb2 = new JLabel("   语调");
        JLabel jb3 = new JLabel("   音量");
        JLabel jb4 = new JLabel("   角色");

        JPanel sel = new JPanel(new GridLayout(4,2));
        sel.add(jb1);
        sel.add(spd);
        sel.add(jb2);
        sel.add(pit);
        sel.add(jb3);
        sel.add(vol);
        sel.add(jb4);
        sel.add(per);

        taLog = new TextArea();
        taLog.setColumns(30);
        taLog.setRows(150);
        taLog.setBackground(Color.CYAN);
        taLog.setFont(new Font("宋体",Font.BOLD,16));
        taLog.setEditable(false);

        // 创建相关的Label标签
        JLabel labelStart = new JLabel("请输入文本:");
        JLabel labelTip1 = new JLabel("仅供测试
如有问题请联系
QQ772173722"); //提示输入文本 JPanel inputTxt = new JPanel(new GridLayout(2, 1)); inputTxt.add(labelStart,BorderLayout.CENTER); inputTxt.add(txt,BorderLayout.CENTER); //开始按钮及左右信息 JPanel panel4 = new JPanel(new GridLayout(1, 3)); panel4.add(labelTip1); panel4.add(jbtStart); panel4.add(jbtReset); JPanel log = new JPanel(new GridLayout(1,1)); tips = new JLabel(); log.add(tips); //上级布局 JPanel panel22 = new JPanel(new GridLayout(4,1)); panel22.add(sel); panel22.add(inputTxt); panel22.add(panel4); panel22.add(tips); jFrame.setLayout(new BorderLayout()); jFrame.add(panel22, BorderLayout.CENTER); // 初始化JFrame窗口 jFrame.setLocation(800, 300); jFrame.setSize(600, 500); jFrame.setBackground(Color.darkGray); jFrame.setResizable(true); jFrame.setVisible(true); } /** * 点击监听器 * @param view */ @Override public void actionPerformed(ActionEvent view) { if (view.getSource() == jbtStart) { String spdStr =spd.getSelectedItem().toString(); String pitStr = pit.getSelectedItem().toString(); String volStr = vol.getSelectedItem().toString(); String perStr = per.getSelectedItem().toString(); String txtStr =txt.getText() ; if (!StringUtils.isEmpty(spdStr) && !StringUtils.isEmpty(pitStr) && !StringUtils.isEmpty(volStr) && !StringUtils.isEmpty(perStr) && !StringUtils.isEmpty(txtStr)) { makeSpeech(spdStr,pitStr,volStr,perStr,txtStr); } }else if(view.getSource() == jbtReset){ //重置组件内容 tips.setText(null); txt.setText("我是文本"); spd.setSelectedIndex(0); per.setSelectedIndex(0); pit.setSelectedIndex(0); vol.setSelectedIndex(0); } } /** * 合成语音 * @param spdStr * @param pitStr * @param volStr * @param perStr * @param txtStr */ private void makeSpeech(String spdStr, String pitStr, String volStr, String perStr, String txtStr) { String fileName = ConstansHelper.makeSpeech(spdStr, pitStr, volStr, perStr, txtStr); if(!StringUtils.isEmpty(fileName)){ //返回值 ,0=是,1=否,2=取消 int dialog = JOptionPane.showConfirmDialog(null, "合成成功!是否打开文件所在位置?"); tips.setText("文件位置: "+fileName); if(dialog == 0) { try { String open = fileName.substring(0, fileName.lastIndexOf("\\")); //打开文件所在位置 Runtime.getRuntime().exec("explorer.exe " + open); } catch (IOException e) { e.printStackTrace(); } } } } @Override public void windowOpened(WindowEvent e) { } @Override public void windowClosing(WindowEvent e) { System.exit(0); } @Override public void windowClosed(WindowEvent e) { System.exit(0); } @Override public void windowIconified(WindowEvent e) { } @Override public void windowDeiconified(WindowEvent e) { } @Override public void windowActivated(WindowEvent e) { } @Override public void windowDeactivated(WindowEvent e) { } public static void main(String[] args) { Container container = new Container(); container.run(); } }

3.生成Jar包

(1)默认情况下项目设置project structure(快捷键ctrl+alt+shift+S)里是没有artifacts的,我们新增一个
利用百度API实现语音合成_第4张图片
(2)选择main方法所在的类
利用百度API实现语音合成_第5张图片
注:如果出现"点击jar包没有反应,没有清单文件主函数"的问题,修改目录到src下即可
修改前:在这里插入图片描述
修改后:在这里插入图片描述
(3)此时build一下就可以啦
利用百度API实现语音合成_第6张图片
利用百度API实现语音合成_第7张图片

你可能感兴趣的:(笔记)