讯飞语音唤醒+语音识别+语音合成+文生图完整集成实战

在人工智能快速发展的今天,语音交互已成为人机交互的重要方式。本文将详细介绍如何基于讯飞开放平台,实现一个完整的语音交互系统,包含语音唤醒、语音识别(流式听写)、语音合成(TTS)和文生图功能。通过本文,你将学会如何将这些能力整合到一个Java应用中,打造一个能够"听懂"用户说话并生成图片的智能助手。

一、项目概述

1.1 功能流程

本项目的核心流程如下:

  1. 语音唤醒:持续监听麦克风,检测唤醒词

  2. 语音识别:唤醒后将用户语音实时转换为文字

  3. 文生图:根据识别出的文字描述生成图片

  4. 语音合成:将操作结果通过语音播报给用户

1.2 技术栈

  • Java 8+

  • 讯飞开放平台API(语音唤醒、语音听写、语音合成、星火文生图)

  • WebSocket(实时通信)

  • JNA(调用讯飞SDK)

  • Java Sound API(音频采集与播放)

  • OkHttp3(HTTP请求)

  • Gson(JSON解析)

二、环境准备

2.1 讯飞开放平台配置

  1. 注册讯飞开放平台账号

  2. 创建应用,获取以下凭证:

    • APPID

    • APIKey

    • APISecret

  3. 开通以下服务:

    • 语音唤醒(离线命令词)

    • 语音听写(流式版)

    • 语音合成(流式版)

    • 星火文生图

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;
}

七、优化建议

  1. 异步处理:将耗时的文生图操作放在线程池中执行

  2. 缓存机制:对常用图片生成结果进行缓存

  3. 错误重试:网络请求失败时自动重试

  4. 日志记录:完善日志输出,便于问题排查

  5. 资源管理:确保音频设备正确关闭,避免资源泄露

八、总结

本文详细介绍了如何基于讯飞开放平台构建一个完整的语音交互系统。通过整合语音唤醒、语音识别、语音合成和文生图功能,我们实现了一个能够听懂用户指令并生成图片的智能助手。

这个系统具有良好的扩展性,可以在此基础上增加更多功能,如:

  • 接入大语言模型实现智能对话

  • 增加图像处理功能

  • 支持多语言识别

  • 集成更多AIGC能力

希望本文能够帮助开发者快速上手讯飞开放平台的各项能力,构建出更多创新的语音交互应用。

相关推荐
码农小白AI2 小时前
AI报告文档审核助力排气烟度精准管控:IACheck守护绿色动力环境与合规发展新底线
大数据·人工智能
深圳市快瞳科技有限公司2 小时前
高精度宠物鼻纹识别算法原理解析:从图像采集到特征匹配
人工智能·计算机视觉·智慧城市
DX_水位流量监测2 小时前
德希科技在线 pH 传感器
人工智能·科技·水质监测·水质传感器·水质厂家·供水水质监测·污水监测
热点速递2 小时前
苹果首款AI穿戴硬件“Apple Pin”曝光:配iPhone的“AI眼睛”,能否突破独立局限?
人工智能·业界资讯
Java后端的Ai之路2 小时前
Milvus 向量数据库从入门到精通:AI 时代的“记忆中枢“实战指南(建议收藏!)
数据库·人工智能·milvus·向量数据库·rag
xixixi777772 小时前
AI的“血管”:从大模型需求看6G、高速光纤与智算中心网络的技术变革
人工智能·ai·大模型·算力·通信·光纤·政策
AI科技星2 小时前
光速螺旋量子几何统一场论——基于 v ≡ c 公理的四大基本力全维度求导证明与精准数值验证
c语言·开发语言·人工智能·算法·机器学习·平面
云烟成雨TD2 小时前
Spring AI 1.x 系列【17】函数型工具开发与使用
java·人工智能·spring