资讯 文档
技术能力
语音技术
文字识别
人脸与人体
图像技术
语言与知识
视频技术

流式文本在线合成JAVA SDK

快速入门

安装Speech Java SDK

Speech Java SDK目录结构

com.baidu.aip
       ├── auth                                //签名相关类
       ├── http                                //Http通信相关类
       ├── client                              //公用类
       ├── exception                           //exception类
       ├── speech
       │       └── AipSpeech           //AipSpeech类
       └── util                                //工具类

支持 JAVA版本:1.7+

查看源码

Java SDK代码现已公开,您可以查看代码、或者在License范围内修改和编译SDK以适配您的环境。请点击此处下载java sdk源代码。

使用maven依赖

添加以下依赖即可。其中版本号可在maven官网查询

<dependency>
    <groupId>com.baidu.aip</groupId>
    <artifactId>java-sdk</artifactId>
    <version>${version}</version>
</dependency>

直接使用JAR包步骤如下

1.在官方网站下载识别、合成 RESTful API Java SDK压缩工具包。

2.将下载的aip-java-sdk-version.zip解压后,复制到工程文件夹中。

3.在Eclipse右键“工程 -> Properties -> Java Build Path -> Add JARs”。

4.添加SDK工具包aip-java-sdk-version.jar和第三方依赖工具包json-20160810.jar log4j-1.2.17.jar

其中,version为版本号,添加完成后,用户就可以在工程中使用Speech Java SDK。

新建AipSpeech

AipSpeech是语音识别与合成的Java客户端,为使用语音合成的开发人员提供了一系列的交互方法。

用户可以参考如下代码新建一个AipSpeech,初始化完成后建议单例使用,避免重复获取access_token:

public class StreamTtsExample {
    // 设置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";
    private static final Logger LOGGER = LoggerFactory.getLogger(StreamTtsExample.class);

    public static void main(String[] args) {
        // 初始化一个AipSpeech
        AipSpeech client = new AipSpeech(APP_ID, API_KEY, SECRET_KEY);

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

        // 调用流式合成接口
        StreamTtsSynthesis(client);
    }

    private static void StreamTtsSynthesis(AipSpeech client) {
        // 创建请求参数
        StreamTtsRequest request = new StreamTtsRequest();
        request.setSpd(5); // 语速,取值0-15,默认为5中语速
        request.setPit(5); // 音调,取值0-15,默认为5中语调
        request.setVol(5); // 音量,取值0-15,默认为5中音量
        request.setPer(4146); // 发音人选择, 4146:百度丫丫-情感女声
        request.setAue(3); // 音频格式选择 3:mp3格式(默认) 4:pcm-16k 5:pcm-8k 6:wav格式
        // 采样率,仅支持将采样率降采为16k。
        // request.setSamplingRate();

        // 要合成的文本片段数组
        String[] texts = {
                "飞船在深空中缓缓滑行,舷窗外的星云泛着微",
                "弱的蓝光。指挥官林远重新校准了导航系统,",
                "确保他们不会偏离既定航道。远处,一颗无名",
                "行星的影子逐渐浮现,其表面布满裂痕,仿佛",
                "经历过无数次天体撞击。科研员苏晴调出了能",
                "量探测图,发现地壳深处竟有稳定的能量脉冲",
                "反应。她压低声音对林远说:“长官,我们可",
                "能发现了一种未知文明的遗迹。”众人心头猛",
                "地一震,飞船上的气氛顿时紧张起来,所有仪",
                "器的读数都在快速变化,像在预示着什么即将",
                "发生。飞船继续逼近,那股神秘能量愈发清晰。"
        };
      
      // 一次性输入
        // String[] texts = {"飞船在深空中缓缓滑行,舷窗外的星云泛着微弱的蓝光。指挥官林远重新校准了导航系统,确保他们不会偏离既定航道。远处,一颗无名行星的影子逐渐浮现,其表面布满裂痕,仿佛经历过无数次天体撞击。科研员苏晴调出了能量探测图,发现地壳深处竟有稳定的能量脉冲反应。她压低声音对林远说:“长官,我们可能发现了一种未知文明的遗迹。”众人心头猛地一震,飞船上的气氛顿时紧张起来,所有仪器的读数都在快速变化,像在预示着什么即将发生。飞船继续逼近,那股神秘能量愈发清晰。"};

        // 文件合成路径
        String outputPath = "output.mp3";

        // 创建同步锁存器
        CountDownLatch completionLatch = new CountDownLatch(1);

        // 创建监听器
        StreamTtsListener listener = new StreamTtsListener() {
            private FileOutputStream fos;

            @Override
            public void onSynthesisStart(String sessionId) {
                LOGGER.info("Stream synthesis started, sessionId: {}", sessionId);
                try {
                    fos = new FileOutputStream(outputPath);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onAudioData(String sessionId, byte[] data) {
                LOGGER.info("Received audio data, length: {} byte, sessionId: {}", data.length, sessionId);
                // 将音频数据写入文件
                try {
                    if (fos != null) {
                        fos.write(data);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onSynthesisEnd(String sessionId) {
                LOGGER.info("Stream synthesis ended, sessionId: {}", sessionId);
                try {
                    if (fos != null) {
                        fos.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                LOGGER.info("Audio file saved as {}", outputPath);
                // 释放锁存器
                completionLatch.countDown();
            }

            @Override
            public void onSynthesisFail(JSONObject response) {
                LOGGER.error("Stream synthesis failed: {}", response.toString());
                try {
                    if (fos != null) {
                        fos.close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    // 出现错误也释放锁存器
                    completionLatch.countDown();
                }
            }
        };

        // 创建流式合成器
        StreamTtsSession session = null;
        try {

            session = client.streamSynthesis(client, request, listener);

            // 启动流式合成
            session.start();

            // 发送文本片段
            for (String text : texts) {
                LOGGER.info("Send Text Request: {}", text);
                session.process(text);
                // 模拟用户间隔输入文本的场景
                Thread.sleep(500);
            }

            // 停止流式合成,默认时间为60s,可根据文本长度自定义时间
            session.stop(); // 可传入参数 60000
            LOGGER.info("Stream synthesis stopped!");

            // 等待合成完成,最多等待120秒,可根据文本长度自定义时间
            if (completionLatch.await(120, TimeUnit.SECONDS)) {
                LOGGER.info("Synthesis completed!");
            } else {
                LOGGER.error("Synthesis timeout!");
            }
        } catch (Exception e) {
            LOGGER.error("Error processing: {}", e.getMessage(), e);
            e.printStackTrace();
        } finally {
            if (session != null) {
                session.close(); //关闭连接
            }
        }
    }
}

在上面代码中,常量APP_ID在百度云控制台中创建,常量API_KEYSECRET_KEY是在创建完毕应用后,系统分配给用户的,均为字符串,用于标识用户,为访问做签名验证,可在AI服务控制台中的应用列表中查看。

配置AipSpeech

如果用户需要配置AipSpeech的一些细节参数,可以在构造AipSpeech之后调用接口设置参数,目前只支持以下参数:

接口 说明
setConnectionTimeoutInMillis 建立连接的超时时间(单位:毫秒)
setSocketTimeoutInMillis 通过打开的连接传输数据的超时时间(单位:毫秒)
setHttpProxy 设置http代理服务器
setSocketProxy 设置socket代理服务器 (http和socket类型代理服务器只能二选一)

接口说明

流式语音合成

详细文档参考官网:https://ai.baidu.com/ai-doc/SPEECH/lm5xd63rn

接口描述

基于该接口,开发者可以轻松获取流式语音合成能力。流式合成相比普通合成具有以下优势:

  • 实时性更强:边合成边播放,减少等待时间
  • 内存占用更小:无需等待全部合成完毕再获取音频
  • 用户体验更好:可以更快听到第一段语音内容
功能说明
  • 建议文本不超过2000 GBK字节,即1000个汉字或者字母数字
  • 输入的文本必须采用UTF-8编码
  • 支持多音字通过标注自行定义发音。格式如:重(chong2)报集团

请求说明

流式语音合成采用WebSocket协议进行通信,支持长文本分段传输合成。 Java Demo 如下:

其中,文本支持分段输入,也支持一次性输入,但需注意长度限制。

import com.baidu.aip.speech.AipSpeech;
import com.baidu.aip.speech.StreamTtsListener;
import com.baidu.aip.speech.StreamTtsRequest;
import com.baidu.aip.speech.StreamTtsSession;
import org.json.JSONObject;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 流式TTS合成示例
 */
public class StreamTtsExample {
    // 设置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";
    private static final Logger LOGGER = LoggerFactory.getLogger(StreamTtsExample.class);

    public static void main(String[] args) {
        // 初始化一个AipSpeech
        AipSpeech client = new AipSpeech(APP_ID, API_KEY, SECRET_KEY);

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

        // 调用流式合成接口
        StreamTtsSynthesis(client);
    }

    private static void StreamTtsSynthesis(AipSpeech client) {
        // 创建请求参数
        StreamTtsRequest request = new StreamTtsRequest();
        request.setSpd(5); // 语速,取值0-15,默认为5中语速
        request.setPit(5); // 音调,取值0-15,默认为5中语调
        request.setVol(5); // 音量,取值0-15,默认为5中音量
        request.setPer(4146); // 发音人选择, 4146:百度丫丫-情感女声
        request.setAue(3); // 音频格式选择 3:mp3格式(默认) 4:pcm-16k 5:pcm-8k 6:wav格式
        // 采样率,仅支持将采样率降采为16k。
        // request.setSamplingRate();

        // 要合成的文本片段数组
        String[] texts = {
                "飞船在深空中缓缓滑行,舷窗外的星云泛着微",
                "弱的蓝光。指挥官林远重新校准了导航系统,",
                "确保他们不会偏离既定航道。远处,一颗无名",
                "行星的影子逐渐浮现,其表面布满裂痕,仿佛",
                "经历过无数次天体撞击。科研员苏晴调出了能",
                "量探测图,发现地壳深处竟有稳定的能量脉冲",
                "反应。她压低声音对林远说:“长官,我们可",
                "能发现了一种未知文明的遗迹。”众人心头猛",
                "地一震,飞船上的气氛顿时紧张起来,所有仪",
                "器的读数都在快速变化,像在预示着什么即将",
                "发生。飞船继续逼近,那股神秘能量愈发清晰。"
        };
      
      // 一次性输入
        // String[] texts = {"飞船在深空中缓缓滑行,舷窗外的星云泛着微弱的蓝光。指挥官林远重新校准了导航系统,确保他们不会偏离既定航道。远处,一颗无名行星的影子逐渐浮现,其表面布满裂痕,仿佛经历过无数次天体撞击。科研员苏晴调出了能量探测图,发现地壳深处竟有稳定的能量脉冲反应。她压低声音对林远说:“长官,我们可能发现了一种未知文明的遗迹。”众人心头猛地一震,飞船上的气氛顿时紧张起来,所有仪器的读数都在快速变化,像在预示着什么即将发生。飞船继续逼近,那股神秘能量愈发清晰。"};

        // 文件合成路径
        String outputPath = "output.mp3";

        // 创建同步锁存器
        CountDownLatch completionLatch = new CountDownLatch(1);

        // 创建监听器
        StreamTtsListener listener = new StreamTtsListener() {
            private FileOutputStream fos;

            @Override
            public void onSynthesisStart(String sessionId) {
                LOGGER.info("Stream synthesis started, sessionId: {}", sessionId);
                try {
                    fos = new FileOutputStream(outputPath);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onAudioData(String sessionId, byte[] data) {
                LOGGER.info("Received audio data, length: {} byte, sessionId: {}", data.length, sessionId);
                // 将音频数据写入文件
                try {
                    if (fos != null) {
                        fos.write(data);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onSynthesisEnd(String sessionId) {
                LOGGER.info("Stream synthesis ended, sessionId: {}", sessionId);
                try {
                    if (fos != null) {
                        fos.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                LOGGER.info("Audio file saved as {}", outputPath);
                // 释放锁存器
                completionLatch.countDown();
            }

            @Override
            public void onSynthesisFail(JSONObject response) {
                LOGGER.error("Stream synthesis failed: {}", response.toString());
                try {
                    if (fos != null) {
                        fos.close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    // 出现错误也释放锁存器
                    completionLatch.countDown();
                }
            }
        };

        // 创建流式合成器
        StreamTtsSession session = null;
        try {

            session = client.streamSynthesis(client, request, listener);

            // 启动流式合成
            session.start();

            // 发送文本片段
            for (String text : texts) {
                LOGGER.info("Send Text Request: {}", text);
                session.process(text);
                // 模拟用户间隔输入文本的场景
                Thread.sleep(500);
            }

            // 停止流式合成,默认时间为60s,可根据文本长度自定义时间
            session.stop(); // 可传入参数 60000
            LOGGER.info("Stream synthesis stopped!");

            // 等待合成完成,最多等待120秒,可根据文本长度自定义时间
            if (completionLatch.await(120, TimeUnit.SECONDS)) {
                LOGGER.info("Synthesis completed!");
            } else {
                LOGGER.error("Synthesis timeout!");
            }
        } catch (Exception e) {
            LOGGER.error("Error processing: {}", e.getMessage(), e);
            e.printStackTrace();
        } finally {
            if (session != null) {
                session.close(); //关闭连接
            }
        }
    }
}

StreamTtsRequest参数详解

参数名称 类型 是否必填 说明
spd int 可选 语速,取值 0-15,默认为 5
pit int 可选 音调,取值 0-15,默认为 5
vol int 可选 音量,基础音库取值0-9,其他音库取值 0-15,默认为 5
per int 必填 发音人选择,具体值参考官方文档
aue int 可选 音频格式,3=mp3-16k/24k,4=pcm-16k/24k,5=pcm-8k,6=wav-16k/24k,默认为3
audio_ctrl string 可选 采样率,仅支持将采样率降采为16k。(格式:{"sampling_rate":16000})

StreamTtsListener回调方法

方法 说明
onSynthesisStart 当WebSocket连接打开,服务端返回开始合成响应时调用
onAudioData 当收到音频数据时调用
onSynthesisEnd 当合成结束时调用
onSynthesisFail 当发生错误时调用

返回说明

流式语音合成通过监听器回调方式返回结果:

  1. onSynthesisStart: 合成开始通知
  2. onAudioData: 音频数据,可直接用于播放或保存
  3. onSynthesisEnd: 合成结束通知
  4. onSynthesisFail: 错误信息,包含错误码和错误描述

错误处理

流式TTS合成过程中可能出现多种类型的错误:

  1. 连接错误:WebSocket连接失败
  2. 认证错误:access_token无效或过期
  3. 参数错误:请求参数不符合规范
  4. 服务端错误:服务器内部错误
  5. 网络超时:请求超时

建议在onSynthesisFail回调中进行相应处理。

注意事项

  1. 会话管理:每个StreamTtsSynthesizer实例代表一个独立的合成会话,请勿并发使用同一实例
  2. 资源释放:使用完毕后务必调用synthesizer.close()方法释放连接资源
  3. 超时设置:合理设置start()和stop()方法的超时时间
  4. 文本分割:长文本建议按语义分割成适当长度的片段分别发送
  5. 异常处理:注意捕获并处理可能抛出的各种异常,包括网络异常、参数异常等
  6. 音频格式:根据实际需求选择合适的音频格式,不同格式的数据大小和质量有所差异
上一篇
语音合成 Linux SDK
下一篇
长文本在线合成JAVA SDK