流式文本在线合成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_KEY与SECRET_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 | 当发生错误时调用 |
返回说明
流式语音合成通过监听器回调方式返回结果:
- onSynthesisStart: 合成开始通知
- onAudioData: 音频数据,可直接用于播放或保存
- onSynthesisEnd: 合成结束通知
- onSynthesisFail: 错误信息,包含错误码和错误描述
错误处理
流式TTS合成过程中可能出现多种类型的错误:
- 连接错误:WebSocket连接失败
- 认证错误:access_token无效或过期
- 参数错误:请求参数不符合规范
- 服务端错误:服务器内部错误
- 网络超时:请求超时
建议在onSynthesisFail回调中进行相应处理。
注意事项
- 会话管理:每个StreamTtsSynthesizer实例代表一个独立的合成会话,请勿并发使用同一实例
- 资源释放:使用完毕后务必调用synthesizer.close()方法释放连接资源
- 超时设置:合理设置start()和stop()方法的超时时间
- 文本分割:长文本建议按语义分割成适当长度的片段分别发送
- 异常处理:注意捕获并处理可能抛出的各种异常,包括网络异常、参数异常等
- 音频格式:根据实际需求选择合适的音频格式,不同格式的数据大小和质量有所差异
