在人工智能快速发展的今天,语音交互已成为人机交互的重要方式。本文将详细介绍如何基于讯飞开放平台,实现一个完整的语音交互系统,包含语音唤醒、语音识别(流式听写)、语音合成(TTS)和文生图功能。通过本文,你将学会如何将这些能力整合到一个Java应用中,打造一个能够"听懂"用户说话并生成图片的智能助手。
一、项目概述
1.1 功能流程
本项目的核心流程如下:
-
语音唤醒:持续监听麦克风,检测唤醒词
-
语音识别:唤醒后将用户语音实时转换为文字
-
文生图:根据识别出的文字描述生成图片
-
语音合成:将操作结果通过语音播报给用户
1.2 技术栈
-
Java 8+
-
讯飞开放平台API(语音唤醒、语音听写、语音合成、星火文生图)
-
WebSocket(实时通信)
-
JNA(调用讯飞SDK)
-
Java Sound API(音频采集与播放)
-
OkHttp3(HTTP请求)
-
Gson(JSON解析)
二、环境准备
2.1 讯飞开放平台配置
-
注册讯飞开放平台账号
-
创建应用,获取以下凭证:
-
APPID
-
APIKey
-
APISecret
-
-
开通以下服务:
-
语音唤醒(离线命令词)
-
语音听写(流式版)
-
语音合成(流式版)
-
星火文生图
-
2.2 项目依赖
<dependencies>
<!-- 讯飞语音唤醒SDK -->
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.12.1</version>
</dependency>
<!-- OkHttp3 -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.3</version>
</dependency>
<!-- Gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
三、核心功能实现
3.1 语音唤醒(IvwService)
语音唤醒是整个系统的入口,需要持续监听麦克风输入,检测到唤醒词后触发后续流程。
public class AIMain {
public static boolean ivwFlag = false;
public static byte[] audioDataByteArray;
public static int len;
public static void startIvw() {
// 1. 登录讯飞平台
Integer ret = IvwService.INSTANCE.MSPLogin(null, null, Constants.IVW_LOGIN_PARAMS);
// 2. 开启唤醒会话
String sessionId = IvwService.INSTANCE.QIVWSessionBegin(null,
Constants.IVW_SSB_PARAMS, Constants.IVW_ERROR_CODE);
// 3. 注册唤醒回调
IvwCallback ivwCallback = new IvwCallback();
ret = IvwService.INSTANCE.QIVWRegisterNotify(sessionId, ivwCallback, null);
// 4. 打开麦克风并持续读取音频
Constants.IVW_ASR_TARGET_DATA_LINE.open(Constants.IVW_ASR_AUDIO_FORMAT);
Constants.IVW_ASR_TARGET_DATA_LINE.start();
while (true) {
audioDataByteArray = new byte[Constants.IVW_FRAME_SIZE];
len = new AudioInputStream(Constants.IVW_ASR_TARGET_DATA_LINE)
.read(audioDataByteArray);
// 写入音频数据
ret = IvwService.INSTANCE.QIVWAudioWrite(sessionId,
audioDataByteArray, len, Constants.IVW_AUDIO_STATUS);
// 检测唤醒标志或超时
if (ivwFlag || (ivwEndTime - ivwStartTime) > 60000) {
// 结束会话并重新启动唤醒监听
IvwService.INSTANCE.QIVWSessionEnd(sessionId, "");
IvwService.INSTANCE.MSPLogout();
startIvw(); // 递归重启
break;
}
Thread.sleep(200);
}
}
}
3.2 语音识别(IatMic)
当唤醒成功后,启动语音识别,将用户的语音实时转换为文字。
public class IatMic extends WebSocketListener {
// 音频帧状态常量
public static final int StatusFirstFrame = 0; // 第一帧
public static final int StatusContinueFrame = 1; // 中间帧
public static final int StatusLastFrame = 2; // 最后一帧
@Override
public void onOpen(WebSocket webSocket, Response response) {
new Thread(() -> {
while (true) {
switch (status) {
case StatusFirstFrame:
// 构建第一帧数据,包含common、business、data
JsonObject frame = buildFirstFrame();
webSocket.send(frame.toString());
status = StatusContinueFrame;
break;
case StatusContinueFrame:
// 发送中间帧音频数据
JsonObject frame1 = buildContinueFrame();
webSocket.send(frame1.toString());
break;
}
if (!IAT_FLAG) break;
Thread.sleep(200);
}
// 发送最后一帧
sendLastFrame(webSocket);
}).start();
}
@Override
public void onMessage(WebSocket webSocket, String text) {
ResponseData resp = json.fromJson(text, ResponseData.class);
if (resp.getData().getStatus() == 2) {
// 识别完成,获取最终识别结果
String resultText = decoder.toString();
System.out.println("识别结果:" + resultText);
// 调用文生图功能
TextProductImage.doWork(resultText);
// 恢复唤醒监听
AIMain.ivwFlag = true;
webSocket.close(1000, "");
}
}
}
3.3 文生图(TextProductImage)
根据识别的文字描述,调用讯飞星火文生图API生成图片。
public class TextProductImage {
public static void doWork(String content) throws Exception {
// 1. 获取鉴权URL
String authUrl = getAuthUrl(hostUrl, apiKey, apiSecret);
// 2. 构建请求JSON
String json = buildRequestJson(content);
// 3. 发送POST请求
String res = MyUtil.doPostJson(authUrl, null, json);
// 4. 解析响应,获取Base64编码的图片
JsonParse jsonParse = gson.fromJson(res, JsonParse.class);
byte[] imageBytes = Base64.getDecoder().decode(
jsonParse.payload.choices.text.get(0).content);
// 5. 保存图片文件
String outputPath = "src/main/resources/generated.png";
try (FileOutputStream out = new FileOutputStream(outputPath)) {
out.write(imageBytes);
}
// 6. 自动打开图片并语音播报
Desktop.getDesktop().open(new File(outputPath));
Tts.ttsWork("已为您成功生成图片");
}
private static String getAuthUrl(String hostUrl, String apiKey, String apiSecret)
throws Exception {
URL url = new URL(hostUrl);
SimpleDateFormat format = new SimpleDateFormat(
"EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("GMT"));
String date = format.format(new Date());
// 构建签名
String preStr = "host: " + url.getHost() + "\n" +
"date: " + date + "\n" +
"POST " + url.getPath() + " HTTP/1.1";
Mac mac = Mac.getInstance("hmacsha256");
SecretKeySpec spec = new SecretKeySpec(
apiSecret.getBytes(StandardCharsets.UTF_8), "hmacsha256");
mac.init(spec);
String sha = Base64.getEncoder().encodeToString(
mac.doFinal(preStr.getBytes(StandardCharsets.UTF_8)));
// 拼接鉴权信息
String authorization = String.format(
"api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"",
apiKey, "hmac-sha256", "host date request-line", sha);
// 构建最终URL
return HttpUrl.parse("https://" + url.getHost() + url.getPath())
.newBuilder()
.addQueryParameter("authorization",
Base64.getEncoder().encodeToString(
authorization.getBytes(StandardCharsets.UTF_8)))
.addQueryParameter("date", date)
.addQueryParameter("host", url.getHost())
.build()
.toString();
}
}
3.4 语音合成(Tts)
将操作结果通过语音播报给用户,提升交互体验。
public class Tts extends WebSocketListener {
public static void ttsWork(String text) {
Tts tts = new Tts(text);
String authUrl = getAuthUrl(hostUrl, apiKey, apiSecret);
String url = authUrl.replace("http://", "ws://").replace("https://", "wss://");
OkHttpClient client = new OkHttpClient.Builder().build();
Request request = new Request.Builder().url(url).build();
WebSocket webSocket = client.newWebSocket(request, tts);
}
@Override
public void onOpen(WebSocket webSocket, Response response) {
// 构建合成请求
String requestJson = buildTtsRequest(ttsText);
webSocket.send(requestJson);
// 打开音频播放设备
Constants.TTS_SOURCE_DATA_LINE.open(Constants.TTS_AUDIO_FORMAT);
Constants.TTS_SOURCE_DATA_LINE.start();
}
@Override
public void onMessage(WebSocket webSocket, String text) {
JsonParse resp = gson.fromJson(text, JsonParse.class);
if (resp.data != null && resp.data.audio != null) {
// 解码音频数据并播放
byte[] audioData = Base64.getDecoder().decode(resp.data.audio);
Constants.TTS_SOURCE_DATA_LINE.write(audioData, 0, audioData.length);
if (resp.data.status == 2) {
// 音频发送完毕,关闭连接
Constants.TTS_SOURCE_DATA_LINE.stop();
Constants.TTS_SOURCE_DATA_LINE.close();
webSocket.close(1000, "");
}
}
}
}
四、关键技术点解析
4.1 音频数据处理
系统需要处理两个方向的音频流:
-
输入流:从麦克风采集音频,用于唤醒和识别
-
输出流:播放合成语音
// 音频格式配置
public static final AudioFormat IVW_ASR_AUDIO_FORMAT = new AudioFormat(
16000.0F, // 采样率 16kHz
16, // 位深 16bit
1, // 单声道
true, // 有符号
false // 小端序
);
// 帧大小配置
public static final int IVW_FRAME_SIZE = 640; // 20ms音频数据
4.2 WebSocket实时通信
语音识别和合成都采用WebSocket长连接,实现实时流式传输:
// 连接建立后,持续发送音频数据
while (条件) {
// 读取音频帧
bytesRead = audioInputStream.read(buffer);
// Base64编码后发送
String encoded = Base64.getEncoder().encodeToString(
Arrays.copyOf(buffer, bytesRead));
webSocket.send(buildDataFrame(encoded));
}
4.3 状态管理
系统需要维护多个状态标志,协调不同模块的执行:
public class AIMain {
public static boolean ttsFlag = false; // TTS播放状态
public static boolean ivwFlag = false; // 唤醒触发标志
}
public class IatMic {
public static boolean IAT_FLAG = true; // 识别会话标志
}
public class Tts {
public static boolean ttsWorkingFlag = false; // TTS工作标志
}
五、完整调用流程
1. 启动程序
↓
2. startIvw() - 开启唤醒监听
↓
3. 用户说出唤醒词(如"呼叫大飞")
↓
4. ivwFlag = true,唤醒成功
↓
5. 停止唤醒,启动语音识别
↓
6. IatMic.iatWork() - 建立WebSocket连接
↓
7. 用户说出描述(如"帮我画一只小鸟")
↓
8. 实时识别并返回识别结果
↓
9. TextProductImage.doWork() - 调用文生图API
↓
10. 生成图片并保存到本地
↓
11. Tts.ttsWork() - 语音播报结果
↓
12. 恢复唤醒监听,等待下一次唤醒
六、常见问题与解决方案
6.1 麦克风设备占用
问题:唤醒和识别需要共用麦克风,可能发生资源冲突。
解决方案:
// 使用同一个DataLine实例,通过标志位控制读写
if (ivwFlag) {
// 唤醒成功,停止读取麦克风,交由识别模块处理
Constants.IVW_ASR_TARGET_DATA_LINE.stop();
IatMic.iatWork(); // 识别模块重新读取麦克风
}
6.2 音频格式不匹配
问题:不同服务对音频格式要求不同。
解决方案:
-
统一使用16kHz、16bit、单声道PCM格式
-
确保编码方式正确(raw格式)
6.3 网络超时
问题:WebSocket连接可能因网络问题断开。
解决方案:
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
// 重连机制
if (shouldRetry) {
reconnect();
}
// 恢复唤醒监听
AIMain.ivwFlag = true;
}
七、优化建议
-
异步处理:将耗时的文生图操作放在线程池中执行
-
缓存机制:对常用图片生成结果进行缓存
-
错误重试:网络请求失败时自动重试
-
日志记录:完善日志输出,便于问题排查
-
资源管理:确保音频设备正确关闭,避免资源泄露
八、总结
本文详细介绍了如何基于讯飞开放平台构建一个完整的语音交互系统。通过整合语音唤醒、语音识别、语音合成和文生图功能,我们实现了一个能够听懂用户指令并生成图片的智能助手。
这个系统具有良好的扩展性,可以在此基础上增加更多功能,如:
-
接入大语言模型实现智能对话
-
增加图像处理功能
-
支持多语言识别
-
集成更多AIGC能力
希望本文能够帮助开发者快速上手讯飞开放平台的各项能力,构建出更多创新的语音交互应用。