简单集成百度智能云语音合成技术之长文本语音合成

关于百度智能云账号的注册移步案例:简单集成百度智能云语音合成技术之短文本语音在线合成-CSDN博客

在之前的短文本合成案例中,应对长文本时,语音合成的速度很慢。所以我在网上找资料实现了长文本语音合成方法。废话不多说直接进入依赖配置。

1.pom文件依赖

        <dependency>            
            <groupId>com.baidu.aip</groupId>
            <artifactId>java-sdk</artifactId>
            <version>4.16.11</version>
        </dependency>

        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>3.14.9</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

2.application.yml/application.properties文件配置

baidu.tts.api-key=你的API KEY

baidu.tts.secret-key=你的SECRET KEY

3.service层

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.lxy.Reading.entity.TtsRequest;
import okhttp3.MediaType;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import okhttp3.RequestBody;
import okhttp3.*;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;


@Service
public class LongTextTts {
    @Value("${baidu.tts.api-key}")
    private String apiKey;

    @Value("${baidu.tts.secret-key}")
    private String secretKey;
    private static final OkHttpClient HTTP_CLIENT = new OkHttpClient();


    public String createTTS(TtsRequest request) throws IOException {

        // 构造 JSON 请求体
        JSONObject requestBody = new JSONObject();
        requestBody.put("text",request.getText());
        requestBody.put("format", "mp3-16k");
        requestBody.put("voice", request.getVoiceType());
        requestBody.put("lang", "zh");

        // 打印请求体
        System.out.println("Request Body: " + requestBody.toJSONString());

        MediaType mediaType = MediaType.parse("application/json");
        RequestBody body = RequestBody.create(mediaType, requestBody.toJSONString());

        Request request2 = new Request.Builder()
                .url("https://aip.baidubce.com/rpc/2.0/tts/v1/create?access_token=" + getAccessToken())
                .post(body)
                .addHeader("Content-Type", "application/json")
                .build();

        try (Response response = HTTP_CLIENT.newCall(request2).execute()) {
            if (response.isSuccessful() && response.body() != null) {
                String responseBody = response.body().string();
                System.out.println("Response Body: " + responseBody); // 打印响应体
                JSONObject jsonObject = JSON.parseObject(responseBody);
                return jsonObject.getString("task_id");
            } else {
                if (response.body() != null) {
                    System.out.println("Error Response: " + response.body().string());
                }
                throw new RuntimeException("创建TTS任务失败: " + response.code());
            }
        }
    }

    public String checkTaskStatus(String taskId) throws IOException {
        if (taskId == null || taskId.isEmpty()) {
            throw new IllegalArgumentException("任务ID不能为空");
        }
        String taskStatus = "";
        while (!("Success".equals(taskStatus) || "Failed".equals(taskStatus))) {
            try {
                Thread.sleep(1000); // Sleep for 1 second
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            HashMap<String, Object> map = new HashMap<>();
            ArrayList<Object> list = new ArrayList<>();
            list.add(taskId);
            map.put("task_ids", list);
            JSONObject jsonObject = new JSONObject(map);
            MediaType mediaType = MediaType.parse("application/json");
            RequestBody body = RequestBody.create(mediaType, jsonObject.toString());
            Request request = new Request.Builder()
                    .url("https://aip.baidubce.com/rpc/2.0/tts/v1/query?access_token=" + getAccessToken())
                    .method("POST", body)
                    .addHeader("Content-Type", "application/json")
                    .addHeader("Accept", "application/json")
                    .build();
            try (Response response = HTTP_CLIENT.newCall(request).execute()) {
                if (response.isSuccessful()) {
                    String responseBody = response.body().string();
                    JSONArray tasksInfo = JSON.parseObject(responseBody).getJSONArray("tasks_info");
                    if (tasksInfo.size() > 0) {
                        JSONObject taskInfo = tasksInfo.getJSONObject(0);
                        taskStatus = taskInfo.getString("task_status");
                        if ("Success".equals(taskStatus)) {
                            return taskInfo.getJSONObject("task_result").getString("speech_url");
                        } else if ("Failed".equals(taskStatus)) {
                            throw new RuntimeException("任务失败: " + taskStatus);
                        }
                    } else {
                        throw new RuntimeException("未找到任务信息");
                    }
                } else {
                    throw new RuntimeException("检查任务状态失败: " + response.code());
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return "";
    }

    private String getAccessToken() throws IOException {
        MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
        RequestBody body = RequestBody.create(mediaType, "grant_type=client_credentials&client_id=" + apiKey
                + "&client_secret=" + secretKey);
        Request request = new Request.Builder()
                .url("https://aip.baidubce.com/oauth/2.0/token")
                .method("POST", body)
                .addHeader("Content-Type", "application/x-www-form-urlencoded")
                .build();
        Response response = HTTP_CLIENT.newCall(request).execute();
        if (response.isSuccessful() && response.body() != null) {
            String responseBody = response.body().string();
            JSONObject jsonObject = JSON.parseObject(responseBody);
            return jsonObject.getString("access_token");
        } else {
            throw new IOException("Failed to get access token: " + response.code());
        }
    }

4.实体类

getter、setter注解大家记得自己加

public class TtsRequest {

    private String text;

    private String voiceType;

    private String pitch;

}

5.controller层

@RestController
public class TttsController {
    @Autowired
    private LongTextTts longTextTts;
    
    @RequestMapping("/synthesize")
    public ResponseEntity<String> synthesizeText(@RequestBody TtsRequest request) {
        try {
            String taskId = longTextTts.createTTS(request);
            System.out.println("taskId = " + taskId);
                String audioUrl = longTextTts.checkTaskStatus(taskId);
                return ResponseEntity.ok(audioUrl);

        } catch (Exception e) {
            return ResponseEntity.badRequest().body("语音合成失败: " + e.getMessage());
        }
    }
}

测试页面一起给大家了

html中需要的axios.js文件和vue.js文件大家得去网上找(这里暂不提供

页面:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>智能语音朗读系统</title>
    <!-- 引入样式 -->
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <!-- 引入组件库 -->
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <style>
        .container {
            max-width: 800px;
            margin: 20px auto;
            padding: 20px;
            font-family: "Microsoft YaHei";
        }
        .control-panel {
            margin: 20px 0;
            padding: 15px;
            border: 1px solid #e4e7ed;
            border-radius: 8px;
        }
        .slider-container {
            display: flex;
            align-items: center;
            margin: 15px 0;
        }
        .voice-type {
            margin-top: 15px;
            padding: 10px;
            border-top: 1px solid #ebeef5;
        }
        audio {
            width: 30%;
            margin: 15px 0;
        }
        button {
            background: #409eff;
            color: white;
            border: none;
            padding: 10px 25px;
            border-radius: 4px;
            cursor: pointer;
        }
        button:disabled {
            background: #a0cfff;
            cursor: not-allowed;
        }
        .settings-panel {
            display: none;
            margin-top: 20px;
            padding: 15px;
            border: 1px solid #e4e7ed;
            border-radius: 8px;
        }
        .settings-panel.active {
            display: block;
        }
    </style>
</head>
<body>
<div id="app" class="container">
    <audio :src="audioUrl" controls id="player"></audio>
    <textarea id="text"  v-model="inputText" placeholder="请输入要合成的文本"></textarea>
    <button
            @click="playAudio"
            :disabled="isLoading">  <!-- 防止重复点击 -->

        {{ isLoading ? '生成中...' : '播放' }}
    </button>
    <p v-if="errorMessage" style="color: red;">{{ errorMessage }}</p>
    <button @click="isSettingsActive = !isSettingsActive">设置</button>

    <!--    设置面板-->
    <div class="settings-panel" :class="{ active: isSettingsActive }" v-if="isSettingsActive == true">
        <div class="voice-type">
            <label>选择音色:</label>
            <el-select v-model="voiceType" placeholder="请选择音色">
                <el-option label="标准女声" value="0"></el-option>
                <el-option label="标准男声" value="1"></el-option>
                <el-option label="度小雯-活力女主播" value="4100"></el-option>
                <el-option label="度灵儿-清激女声" value="4119"></el-option>
                <el-option label="度有为-磁性男声" value="4176"></el-option>
                <el-option label="度小萌-软萌妹子" value="111"></el-option>
                <el-option label="度清柔-温柔男神" value="6602"></el-option>
                <el-option label="度小夏-甜美女声" value="4148"></el-option>
                <el-option label="度云萱-旁白女声" value="6221"></el-option>
                <el-option label="度悠然-旁白男声" value="6205"></el-option>
            </el-select>
        </div>
    </div>

</div>
<script>
    var vue = new Vue({
        el: "#app",
        data: {
            inputText: "",     // 绑定输入框内容
            audioUrl: "",      // 存储音频URL
            errorMessage: "",   // 错误信息
            audioElement: null,  // 新增音频元素引用
            isLoading:false,
            voiceType: "0",    // 默认音色
            isSettingsActive: false // 设置面板是否显示
        },
        mounted() {
            // 初始化时获取音频元素引用
            this.audioElement = document.getElementById("player");
            // 添加音频加载完成监听
            this.audioElement.addEventListener('canplaythrough', () => {
                this.audioElement.play().catch(error => {
                    console.log('自动播放被阻止:', error);
                });
            });
        },
        methods: {
            async playAudio() {
                try {
                    this.isLoading = true;
                    this.errorMessage = "";
                    this.audioUrl = ""; // 清空旧地址

                    // 验证输入
                    if (!this.inputText.trim()) {
                        this.errorMessage = "请输入要朗读的内容";
                        return;
                    }

                    // 暂停当前播放并重置
                    this.audioElement.pause();
                    this.audioElement.currentTime = 0;

                    // 构造包含参数的 JSON 对象
                    const requestBody = {
                        text: this.inputText,
                        voiceType: this.voiceType,  // 默认音色
                    };
                    const response = await fetch('/synthesize', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json'
                        },
                        body: JSON.stringify(requestBody) // 发送结构化数据
                    });
                    if (response.ok) {
                        const audioUrl = await response.text();
                        document.getElementById('player').src = audioUrl;
                    } else {
                        alert('语音合成失败: ' + response.statusText);
                    }
                } catch (error) {
                    alert('请求失败: ' + error.message);
                }finally {
                    this.isLoading = false;
                    }
                }
            },
    });
</script>
</body>
</html>

 只需要这些就可以开始测试了,对于音色的选择可移步语音技术网页自己选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值