feat: tts语音生成
This commit is contained in:
@@ -1,28 +1,143 @@
|
||||
import { useChatStore } from "@/stores";
|
||||
import { useChatStore, useTtsStore } from "@/stores";
|
||||
|
||||
// WebSocket
|
||||
export const useWebSocketStore = defineStore("websocket", () => {
|
||||
const websocket = ref<WebSocket>();
|
||||
const connected = ref(false);
|
||||
const chatStore = useChatStore();
|
||||
const ttsStore = useTtsStore();
|
||||
|
||||
const { onlineCount } = storeToRefs(chatStore);
|
||||
|
||||
const onmessage = (e: MessageEvent) => {
|
||||
const data = JSON.parse(e.data);
|
||||
switch (data.type) {
|
||||
case "count":
|
||||
onlineCount.value = data.online_count;
|
||||
break;
|
||||
case "asr_result":
|
||||
chatStore.addMessageToHistory(data.result);
|
||||
// 检查消息类型
|
||||
if (e.data instanceof ArrayBuffer) {
|
||||
// 处理二进制音频数据(兜底处理,新版本应该不会用到)
|
||||
console.log("收到二进制音频数据,大小:", e.data.byteLength);
|
||||
console.warn("收到旧格式的二进制数据,无法确定messageId");
|
||||
// 可以选择忽略或者作为兜底处理
|
||||
} else if (e.data instanceof Blob) {
|
||||
// 如果是Blob,转换为ArrayBuffer(兜底处理)
|
||||
e.data.arrayBuffer().then((buffer: ArrayBuffer) => {
|
||||
console.log("收到Blob音频数据,大小:", buffer.byteLength);
|
||||
console.warn("收到旧格式的Blob数据,无法确定messageId");
|
||||
});
|
||||
} else if (typeof e.data === "string") {
|
||||
// 处理文本JSON消息
|
||||
try {
|
||||
const data = JSON.parse(e.data);
|
||||
switch (data.type) {
|
||||
case "count":
|
||||
onlineCount.value = data.online_count;
|
||||
break;
|
||||
case "asr_result":
|
||||
chatStore.addMessageToHistory(data.result);
|
||||
break;
|
||||
|
||||
// 新的TTS消息格式处理
|
||||
case "tts_audio_data":
|
||||
// 新的音频数据格式,包含messageId和hex格式的音频数据
|
||||
if (data.messageId && data.audioData) {
|
||||
console.log(
|
||||
`收到TTS音频数据 [${data.messageId}],hex长度:`,
|
||||
data.audioData.length
|
||||
);
|
||||
try {
|
||||
// 将hex字符串转换为ArrayBuffer
|
||||
const bytes = data.audioData
|
||||
.match(/.{1,2}/g)
|
||||
?.map((byte: string) => Number.parseInt(byte, 16));
|
||||
if (bytes) {
|
||||
const buffer = new Uint8Array(bytes).buffer;
|
||||
console.log(
|
||||
`转换后的音频数据大小 [${data.messageId}]:`,
|
||||
buffer.byteLength
|
||||
);
|
||||
ttsStore.handleAudioData(buffer, data.messageId);
|
||||
} else {
|
||||
console.error(`音频数据格式错误 [${data.messageId}]`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`音频数据转换失败 [${data.messageId}]:`, error);
|
||||
ttsStore.handleError(
|
||||
`音频数据转换失败: ${error}`,
|
||||
data.messageId
|
||||
);
|
||||
}
|
||||
} else {
|
||||
console.error("tts_audio_data消息格式错误:", data);
|
||||
}
|
||||
break;
|
||||
|
||||
case "tts_audio_complete":
|
||||
// TTS音频传输完成
|
||||
if (data.messageId) {
|
||||
console.log(`TTS音频传输完成 [${data.messageId}]`);
|
||||
ttsStore.finishConversion(data.messageId);
|
||||
} else {
|
||||
console.log("TTS音频传输完成(无messageId)");
|
||||
// 兜底处理,可能是旧格式
|
||||
ttsStore.finishConversion(data.messageId);
|
||||
}
|
||||
break;
|
||||
|
||||
case "tts_complete":
|
||||
// TTS会话结束
|
||||
if (data.messageId) {
|
||||
console.log(`TTS会话结束 [${data.messageId}]`);
|
||||
// 可以添加额外的清理逻辑
|
||||
} else {
|
||||
console.log("TTS会话结束");
|
||||
}
|
||||
break;
|
||||
|
||||
case "tts_error":
|
||||
// TTS错误
|
||||
if (data.messageId) {
|
||||
console.error(`TTS错误 [${data.messageId}]:`, data.message);
|
||||
ttsStore.handleError(data.message, data.messageId);
|
||||
} else {
|
||||
console.error("TTS错误:", data.message);
|
||||
// 兜底处理,可能是旧格式
|
||||
ttsStore.handleError(data.message, data.messageId || "unknown");
|
||||
}
|
||||
break;
|
||||
|
||||
// 保留旧的消息类型作为兜底处理
|
||||
case "tts_audio_complete_legacy":
|
||||
case "tts_complete_legacy":
|
||||
case "tts_error_legacy":
|
||||
console.log("收到旧格式TTS消息:", data.type);
|
||||
// 可以选择处理或忽略
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log("未知消息类型:", data.type, data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("JSON解析错误:", error, "原始数据:", e.data);
|
||||
}
|
||||
} else {
|
||||
console.warn("收到未知格式的消息:", typeof e.data, e.data);
|
||||
}
|
||||
};
|
||||
|
||||
const send = (data: string) => {
|
||||
if (websocket.value && websocket.value.readyState === WebSocket.OPEN)
|
||||
if (websocket.value && websocket.value.readyState === WebSocket.OPEN) {
|
||||
websocket.value?.send(data);
|
||||
} else {
|
||||
console.warn("WebSocket未连接,无法发送消息:", data);
|
||||
}
|
||||
};
|
||||
|
||||
const sendBinary = (data: ArrayBuffer | Uint8Array) => {
|
||||
if (websocket.value && websocket.value.readyState === WebSocket.OPEN) {
|
||||
websocket.value?.send(data);
|
||||
} else {
|
||||
console.warn("WebSocket未连接,无法发送二进制数据");
|
||||
}
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
websocket.value?.close();
|
||||
};
|
||||
@@ -33,11 +148,15 @@ export const useWebSocketStore = defineStore("websocket", () => {
|
||||
|
||||
websocket.value.onopen = () => {
|
||||
connected.value = true;
|
||||
console.log("WebSocket连接成功");
|
||||
|
||||
let pingIntervalId: NodeJS.Timeout | undefined;
|
||||
|
||||
if (pingIntervalId) clearInterval(pingIntervalId);
|
||||
pingIntervalId = setInterval(() => send("ping"), 30 * 1000);
|
||||
pingIntervalId = setInterval(() => {
|
||||
// 修改ping格式为JSON格式,与后端保持一致
|
||||
send(JSON.stringify({ type: "ping" }));
|
||||
}, 30 * 1000);
|
||||
|
||||
if (websocket.value) {
|
||||
websocket.value.onmessage = onmessage;
|
||||
@@ -45,20 +164,28 @@ export const useWebSocketStore = defineStore("websocket", () => {
|
||||
websocket.value.onerror = (e: Event) => {
|
||||
console.error(`WebSocket错误:${(e as ErrorEvent).message}`);
|
||||
};
|
||||
websocket.value.onclose = () => {
|
||||
|
||||
websocket.value.onclose = (e: CloseEvent) => {
|
||||
connected.value = false;
|
||||
console.log(`WebSocket连接关闭: ${e.code} ${e.reason}`);
|
||||
setTimeout(() => {
|
||||
console.log("尝试重新连接WebSocket...");
|
||||
connect(); // 尝试重新连接
|
||||
}, 1000); // 1秒后重试连接
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
websocket.value.onerror = (e: Event) => {
|
||||
console.error("WebSocket连接错误:", e);
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
websocket,
|
||||
connected,
|
||||
send,
|
||||
sendBinary,
|
||||
close,
|
||||
connect
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user