提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
提示:这里可以添加本文要记录的大概内容:
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。
提示:以下是本篇文章正文内容,下面案例可供参考
一、功能介绍
- 官方文档地址
- 我这边是用于手机按下实现实时翻译将翻译内容进行推送前端
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;
}
}
}
}
三、启项目验结果
- 新进行启动接口,我这是关了,你直接发送启动接口 会有成功启动字样

- 接收WebSocket 数据

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