关于阿里云实时语音翻译-Gummy推送WebSocket

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


前言

提示:这里可以添加本文要记录的大概内容:

例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。


提示:以下是本篇文章正文内容,下面案例可供参考

一、功能介绍

  • 官方文档地址
  • 我这边是用于手机按下实现实时翻译将翻译内容进行推送前端

1.配置准备说明

  • apiKey是在你有上角去查看哈准备好
  • .model("gummy-realtime-v1") // 设置模型名 要通过主号进行给这个模版进行授权

二、使用步骤

1.制器层

代码如下(示例):

java 复制代码
package cn.chinaunicom.sdsi.chat.controller;

import cn.chinaunicom.sdsi.chat.service.RealtimeTranslationService;
import cn.chinaunicom.sdsi.framework.base.BaseController;
import cn.chinaunicom.sdsi.framework.response.BaseResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

/**
 * 实时语音翻译控制器层
 */
@RestController
@RequestMapping("/api/translation")
@RequiredArgsConstructor // 自动注入Service
public class RealtimeTranslationController extends BaseController {
    // 注入翻译服务
    private final RealtimeTranslationService translationService;

    /**
     * 启动实时语音翻译
     * @return 启动结果
     */
    @PostMapping("/start")
    public BaseResponse<String> startTranslation() {
        return ok(translationService.start());
    }

    /**
     * 停止实时语音翻译
     * @return 停止结果
     */
    @PostMapping("/stop")
    public BaseResponse<String>  stopTranslation() {
        return ok(translationService.stop());
    }

    /**
     * 查询当前翻译状态
     * @return 运行中/已停止
     */
    @GetMapping("/status")
    public String getTranslationStatus() {
        return "当前翻译状态:" + translationService.getTranslationStatus();
    }

}

2.翻译结果WebSocket推送工具类

java 复制代码
package cn.chinaunicom.sdsi.chat.utils;

import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import java.nio.charset.StandardCharsets;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 翻译结果WebSocket推送工具类
 */
@Component
public class TranslationWsUtil {
    // 线程安全的WebSocket会话列表
    private final CopyOnWriteArrayList<WebSocketSession> sessions = new CopyOnWriteArrayList<>();

    // 新增前端连接
    public void addSession(WebSocketSession session) {
        if (session != null && !sessions.contains(session)) {
            sessions.add(session);
        }
    }

    // 移除前端连接
    public void removeSession(WebSocketSession session) {
        if (session != null) {
            sessions.remove(session);
        }
    }

    /**
     * 核心方法:推送翻译结果给所有前端(强制UTF-8编码)
     * @param text 识别原文
     * @param translateText 翻译结果
     * @param isFinal 是否最终结果
     */
    public void pushResult(String text, String translateText, boolean isFinal) {
        // 1. 构建推送数据(Java 8兼容)
        Map<String, Object> msg = new HashMap<>();
        msg.put("text", text == null ? "" : text);
        msg.put("translate", translateText == null ? "" : translateText);
        msg.put("isFinal", isFinal);

        // 2. 转换为JSON(原生实现,强制UTF-8编码)
        String json = convertToJson(msg);

        // 3. 关键:创建TextMessage时,显式指定UTF-8编码
        // 原错误写法:new TextMessage(json) → 会用系统默认编码(如GBK)
        // 修复后写法:用字节数组+UTF-8编码创建消息
        byte[] utf8Bytes = json.getBytes(StandardCharsets.UTF_8);
        TextMessage textMessage = new TextMessage(utf8Bytes);

        // 4. 推送给所有连接的前端
        for (WebSocketSession session : sessions) {
            if (session != null && session.isOpen()) {
                try {
                    session.sendMessage(textMessage); // 发送UTF-8编码的消息
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    // 原生JSON转换方法(无需修改,保持不变)
    private String convertToJson(Map<String, Object> map) {
        StringBuilder json = new StringBuilder("{");
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();

            json.append("\"").append(key).append("\":");
            if (value instanceof String) {
                // 对中文双引号转义,避免JSON格式错误
                json.append("\"").append(value.toString().replace("\"", "\\\"")).append("\",");
            } else {
                json.append(value).append(",");
            }
        }
        if (json.length() > 1) {
            json.deleteCharAt(json.length() - 1);
        }
        json.append("}");
        return json.toString();
    }
}

3.配置WebSocketConfig

java 复制代码
package cn.chinaunicom.sdsi.chat.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import lombok.RequiredArgsConstructor;

@Configuration
@EnableWebSocket
@RequiredArgsConstructor
public class WebSocketConfig implements WebSocketConfigurer {
    private final TranslationWebSocketHandler handler;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(handler, "/ws/translate")
                .setAllowedOriginPatterns("*"); // 允许跨域
    }
}

4.配置WebSocketHandler

java 复制代码
package cn.chinaunicom.sdsi.chat.config;

import cn.chinaunicom.sdsi.chat.utils.TranslationWsUtil;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
public class TranslationWebSocketHandler extends TextWebSocketHandler {
    private final TranslationWsUtil wsUtil;

    // 前端建立连接时触发
    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        wsUtil.addSession(session);
    }

    // 前端断开连接时触发
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        wsUtil.removeSession(session);
    }
}

5.服务层Impl

代码如下(示例):

java 复制代码
package cn.chinaunicom.sdsi.chat.service.impl;

import cn.chinaunicom.sdsi.chat.service.DialogueService;
import cn.chinaunicom.sdsi.chat.service.RealtimeTranslationService;
import cn.chinaunicom.sdsi.chat.utils.AiFuncConstant;
import cn.chinaunicom.sdsi.chat.utils.TranslationWsUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.dashscope.audio.asr.translation.TranslationRecognizerParam;
import com.alibaba.dashscope.audio.asr.translation.TranslationRecognizerRealtime;
import com.alibaba.dashscope.audio.asr.translation.results.TranslationRecognizerResult;
import com.alibaba.dashscope.common.ResultCallback;
import lombok.extern.slf4j.Slf4j;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.TargetDataLine;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;
/**
 * 实时语音翻译服务层
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class RealtimeTranslationServiceImpl implements RealtimeTranslationService {
    // 目标翻译语言(可通过接口参数动态调整)
    private String targetLanguage = "en";
    // 注入推送工具
    private final TranslationWsUtil wsUtil;
    // 翻译状态开关(线程安全)
    private final AtomicBoolean isTranslating = new AtomicBoolean(false);
    // 音频采集线
    private TargetDataLine targetDataLine;
    // 翻译器实例
    private TranslationRecognizerRealtime translator;

    @Autowired
    private DialogueService dialogueService;
    /**
     * 启动实时翻译
     */
    public String start() {
        if (isTranslating.get()) {
            return "翻译已在运行中";
        }

        try {
            //我这里是通过查询数据库获取key
            String getApiKey = dialogueService.selectByCode(AiFuncConstant.API_KEY);
            // 1. 初始化阿里云翻译参数
            TranslationRecognizerParam param = TranslationRecognizerParam.builder()
                    .apiKey(getApiKey)
                    .model("gummy-realtime-v1")
                    .format("pcm")
                    .sampleRate(16000)
                    .transcriptionEnabled(true)
                    .sourceLanguage("auto")
                    .translationEnabled(true)
                    .translationLanguages(new String[]{targetLanguage})
                    .build();

            // 2. 初始化回调(核心:推送结果到前端)
            ResultCallback<TranslationRecognizerResult> callback = new ResultCallback<TranslationRecognizerResult>() {
                @Override
                public void onEvent(TranslationRecognizerResult result) {
                    // 获取识别/翻译结果
                    String text = result.getTranscriptionResult() != null ? result.getTranscriptionResult().getText() : null;
                    String translateText = result.getTranslationResult() != null ? result.getTranslationResult().getTranslation(targetLanguage).getText() : null;
                    boolean isFinal = result.isSentenceEnd();

                    // 推送给前端(核心一行)
                    wsUtil.pushResult(text, translateText, isFinal);

                    // 日志打印
                    log.info("识别结果:{}(最终:{})", text, isFinal);
                    log.info("翻译结果:{}(最终:{})", translateText, isFinal);
                }

                @Override
                public void onComplete() {
                    log.info("翻译完成");
                    wsUtil.pushResult("", "翻译已完成", true);
                }

                @Override
                public void onError(Exception e) {
                    log.error("翻译异常", e);
                    wsUtil.pushResult("", "翻译异常:" + e.getMessage(), true);
                    stop(); // 异常时自动停止
                }
            };

            // 3. 初始化翻译器和音频采集
            translator = new TranslationRecognizerRealtime();
            translator.call(param, callback);

            AudioFormat audioFormat = new AudioFormat(16000, 16, 1, true, false);
            targetDataLine = AudioSystem.getTargetDataLine(audioFormat);
            targetDataLine.open(audioFormat);
            targetDataLine.start();
            log.info("麦克风已启动,开始采集音频");

            // 4. 标记为运行中,启动音频发送线程
            isTranslating.set(true);
            new Thread(this::sendAudioData, "Audio-Sender").start();

            return "实时翻译已启动";
        } catch (Exception e) {
            log.error("启动失败", e);
            isTranslating.set(false);
            return "启动失败:" + e.getMessage();
        }
    }

    /**
     * 停止实时翻译
     */
    public String stop() {
        if (!isTranslating.get()) {
            return "翻译未运行";
        }

        try {
            // 标记为停止
            isTranslating.set(false);

            // 停止音频采集
            if (targetDataLine != null) {
                targetDataLine.stop();
                targetDataLine.close();
            }

            // 停止翻译器
            if (translator != null) {
                translator.stop();
                translator.getDuplexApi().close(1000, "用户停止");
            }

            log.info("翻译已停止");
            wsUtil.pushResult("", "翻译已停止", true);
            return "实时翻译已停止";
        } catch (Exception e) {
            log.error("停止失败", e);
            return "停止失败:" + e.getMessage();
        }
    }

    /**
     * 异步发送音频数据到阿里云
     */
    private void sendAudioData() {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        while (isTranslating.get()) {
            try {
                int read = targetDataLine.read(buffer.array(), 0, buffer.capacity());
                if (read > 0) {
                    buffer.limit(read);
                    translator.sendAudioFrame(buffer);
                    buffer = ByteBuffer.allocate(1024);
                    Thread.sleep(20); // 降低CPU占用
                }
            } catch (Exception e) {
                log.error("发送音频失败", e);
                break;
            }
        }
    }
  }

三、启项目验结果

  1. 新进行启动接口,我这是关了,你直接发送启动接口 会有成功启动字样
  2. 接收WebSocket 数据

总结

提示:这里对文章进行总结:

例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

相关推荐
晚秋大魔王4 小时前
ubutnu 服务器配置openclaw 使用阿里云百炼模型
运维·服务器·阿里云
江西理工大学小杨6 小时前
高性能 C++ 社交平台4:基于 Boost.Beast 的 WebSocket 网关实现
c++·websocket·微服务
Shacoray6 小时前
OpenClaw 接入阿里云百炼 Coding Plan 指南
阿里云·ai·云计算·qwen3·openclaw·coding plan
TG_yunshuguoji6 小时前
阿里云代理商:2026 年阿里云国际站上云接入指南
服务器·阿里云·云计算
阿里云云原生6 小时前
阿里云可观测 2026 年 1 月产品动态
阿里云·云计算
yezipi耶不耶6 小时前
我在 RTMate 里使用的高并发连接管理利器: DashMap
websocket·rust
柒.梧.8 小时前
基于Netty+WebSocket+DeepSeek AI 实现即时聊天功能
人工智能·websocket·网络协议
TG_yunshuguoji18 小时前
亚马逊云代理商:AWS 国际站缺卡新用户创建邮箱怎么选择?
安全·云计算·aws
峰顶听歌的鲸鱼21 小时前
Zabbix监控系统
linux·运维·笔记·安全·云计算·zabbix·学习方法