Kaynağa Gözat

1、问答前端发音

liyanbo 8 ay önce
ebeveyn
işleme
a6cc8315d8
4 değiştirilmiş dosya ile 103 ekleme ve 17 silme
  1. 2 2
      .env
  2. 72 0
      src/components/TTS/useAudioPlayer.js
  3. 1 1
      src/views/AIPainting.vue
  4. 28 14
      src/views/AIQuestions.vue

+ 2 - 2
.env

@@ -2,8 +2,8 @@
 VITE_APP_TITLE=AI课程网
 
 # 请求路径
-VITE_BASE_URL='http://59.110.91.129/admin-api'
-# VITE_BASE_URL='http://192.168.110.8:8080/admin-api'
+# VITE_BASE_URL='http://59.110.91.129/admin-api'
+VITE_BASE_URL='http://192.168.110.8:8080/admin-api'
 
 # 默认账户密码
 VITE_APP_DEFAULT_LOGIN_TENANT = 博雅智算

+ 72 - 0
src/components/TTS/useAudioPlayer.js

@@ -0,0 +1,72 @@
+import { ref } from 'vue';
+
+export function useAudioPlayer() {
+    let audioContext = null;
+    let audioQueue = [];
+    let isPlaying = false;
+
+    // 初始化AudioContext
+    const initAudioContext = () => {
+        if (!audioContext) {
+            audioContext = new (window.AudioContext || window.webkitAudioContext)({
+                sampleRate: 24000 // 匹配TTS采样率
+            });
+        }
+    };
+
+    // 播放音频块
+    const playAudioChunk = async (base64Audio) => {
+
+        // console.log('playAudioChunk=========', base64Audio);
+        initAudioContext();
+
+        // 解码Base64音频数据
+        const audioBytes = Uint8Array.from(atob(base64Audio), c => c.charCodeAt(0));
+        audioQueue.push(audioBytes);
+
+        if (!isPlaying) {
+            processAudioQueue();
+        }
+    };
+
+    // 处理音频队列
+    const processAudioQueue = async () => {
+        if (audioQueue.length === 0) {
+            isPlaying = false;
+            return;
+        }
+
+        isPlaying = true;
+        const audioData = audioQueue.shift();
+
+        try {
+            const audioBuffer = await audioContext.decodeAudioData(audioData.buffer);
+            const source = audioContext.createBufferSource();
+            source.buffer = audioBuffer;
+            source.connect(audioContext.destination);
+            source.start(0);
+
+            // 播放完成后继续处理队列
+            source.onended = processAudioQueue;
+        } catch (error) {
+            console.error('音频解码失败:', error);
+            isPlaying = false;
+        }
+    };
+
+    // 停止播放并清理
+    const stopPlayback = () => {
+        if (audioContext) {
+            audioContext.close().then(() => {
+                audioContext = null;
+            });
+        }
+        audioQueue = [];
+        isPlaying = false;
+    };
+
+    return {
+        playAudioChunk,
+        stopPlayback
+    };
+}

+ 1 - 1
src/views/AIPainting.vue

@@ -302,7 +302,7 @@ const sendMessage = async() => {
     }
 
     CreatePainting({
-      "modelId": 56,
+      "modelId": 57,
       "prompt":content,
       "width":1024,
       "height":1024

+ 28 - 14
src/views/AIQuestions.vue

@@ -299,6 +299,11 @@ const doSendMessage = async (content) => {
   });
 };
 
+
+import { useAudioPlayer } from '@/components/TTS/useAudioPlayer';
+
+const { playAudioChunk } = useAudioPlayer();
+
 /** 真正执行【发送】消息操作 */
 const doSendMessageStream = async (userMessage) => {
   // 创建 AbortController 实例,以便中止请求
@@ -343,20 +348,30 @@ const doSendMessageStream = async (userMessage) => {
           return;
         }
         // 如果内容为空,就不处理。
-        // if (data.receive.content === '') {
+        // if (data.receive?.content === '') {
         //   return
         // }
-        receiveMessageFullText.value =
-          receiveMessageFullText.value + data.receive.content;
-        // 首次返回需要添加一个 message 到页面,后面的都是更新
-        if (isFirstChunk) {
-          isFirstChunk = false;
-          // 弹出两个假数据
-          activeMessageList.value.pop();
-          activeMessageList.value.pop();
-          // 更新返回的数据
-          activeMessageList.value.push(data.send);
-          activeMessageList.value.push(data.receive);
+
+        // 根据事件类型处理
+        if (data.eventType === 'TEXT') {
+
+          console.log(receiveMessageFullText.value,"========",data.receive.content);
+          // 处理文本消息
+          receiveMessageFullText.value += data.receive.content;
+          // 首次返回需要添加一个 message 到页面,后面的都是更新
+          if (isFirstChunk) {
+            console.log("000000000000000000000000000000000")
+            isFirstChunk = false;
+            // 弹出两个假数据
+            activeMessageList.value.pop();
+            activeMessageList.value.pop();
+            // 更新返回的数据
+            activeMessageList.value.push(data.send);
+            activeMessageList.value.push(data.receive);
+          }
+        } else if (data.eventType === 'AUDIO') {
+          // 处理音频消息
+          await playAudioChunk(data.audioData);
         }
       },
       (error) => {
@@ -446,8 +461,7 @@ const textRoll = async () => {
       }
 
       if (index < receiveMessageFullText.value.length) {
-        receiveMessageDisplayedText.value +=
-          receiveMessageFullText.value[index];
+        receiveMessageDisplayedText.value += receiveMessageFullText.value[index];
         index++;
 
         // 更新 message